Android 2.2 上的 GCM SERVICE_NOT_AVAILABLE

Posted

技术标签:

【中文标题】Android 2.2 上的 GCM SERVICE_NOT_AVAILABLE【英文标题】:GCM SERVICE_NOT_AVAILABLE on Android 2.2 【发布时间】:2013-07-11 05:40:16 【问题描述】:

我在 android 2.2 设备上调用 GoogleCloudMessaging.register() 时收到错误“SERVICE_NOT_AVAILABLE”。

我正在使用新的 Google Play 服务编写一个使用 GoogleCloudMessaging 的应用程序。我使用 Android 网站上提供的指南实现了它,我的源代码包含许多错误检查和处理代码,例如确保安装或更新 Google Play 服务。 GCM 注册代码还按照 google 的建议实现了指数退避,以实现处理 SERVICE_NOT_AVAILABLE 错误。

我已经在各种设备上测试了我的应用程序,包括 ICS、JB、Honey Comb 甚至 2.3.x 设备。 GCM 注册有效,我可以通过 GCM 向它发送消息。但是,在 2.2 设备上,即使有指数退避,我也会在 GoogleCloudMessaging.register() 调用中不断收到 SERVICE_NOT_AVAILABLE 错误。

确切地说,GCM 失败的设备是三星 SGH-I896,手机上有 2 个谷歌帐户。我读过这个错误可能是由错误配置的时间引起的,但时间设置为自动。手机未进行调制,正在运行三星股票 ROM。

我也尝试过重新启动设备以及重新安装 Google Play 服务,但都没有成功。对此问题的任何帮助将不胜感激。

编辑:我尝试使用旧的 gcm.jar 和 GCMRegistrar 实现 GCM,它最终在设备上运行。但是,我几乎不认为这是解决问题的好方法,因为 Google 已经停止支持这种方法了。

EDIT2:查看接受的答案。

【问题讨论】:

【参考方案1】:

我遇到了同样的问题。 GCM 在运行 Android 4.04 的平板电脑上运行良好,但在运行 Android 2.3 的智能手机上总是收到 SERVICE_NOT_AVAILABLE。

我发现以下解决方法不使用(据我所知)任何已弃用的类。将操作“com.google.android.c2dm.intent.REGISTRATION”添加到清单中的 GCMBroadcastReceiver。这将使您能够在您的 GCMBroadcastReceiver 接收registration_id。

<receiver
   android:name="YOUR_PACKAGE_NAME.GCMBroadcastReceiver"
   android:permission="com.google.android.c2dm.permission.SEND" >
      <intent-filter>
         <action android:name="com.google.android.c2dm.intent.RECEIVE" />
         <action android:name="com.google.android.c2dm.intent.REGISTRATION" />

         <category android:name="YOUR_PACKAGE_NAME" />
      </intent-filter>
</receiver>

之后,您的 GCMBroadcastReceiver 就能够接收到 registration_id:

public void onReceive(Context context, Intent intent) 
   String regId = intent.getExtras().getString("registration_id");
   if(regId != null && !regId.equals("")) 
      /* Do what ever you want with the regId eg. send it to your server */
   

虽然我仍然收到 SERVICE_NOT_AVAILABLE 错误,但我可以在我的 GCMBroadcastReceiver 中处理registration_id,并且可以向我的智能手机发送消息。很奇怪,但它对我有用。

【讨论】:

@marnaish - 你是在 google 的广播接收器类中还是在我们自己的广播接收器中编写的? 它是与接收消息的广播接收器相同的广播接收器。 “android:name”参数应该链接到你的广播接收器(我已经改变了它)。如果您按照教程@developer.android.com/google/gcm/gs.html 进行操作,您将获得 GcmBroadcastReceiver 类中的注册 ID。 @marnaish 好的,谢谢!但它仍然无法正常工作。仍然出错 哇...这出乎意料地有效。谢谢! @Sunil Mishra 您仍然会收到错误消息,但它会在 GcmBroadcastReceiver 的 onRecieve 方法中将注册 ID 返回给您。 我在下面添加了一个替代解决方案,请参阅***.com/a/19176015/816017【参考方案2】:

这个错误也发生在我身上,但它确实是因为我试图通过我公司的防火墙在 GCM 中注册。在我发现的文档中:

"注意:如果您的组织有限制流量的防火墙 到 Internet 或从 Internet,您需要将其配置为允许 与 GCM 连接,以便您的 Android 设备接收 消息。要打开的端口是:5228、5229 和 5230。通常是 GCM 仅使用 5228,但有时使用 5229 和 5230。GCM 不使用 提供特定的 IP,所以你应该允许你的防火墙接受 到 IP 块中包含的所有 IP 地址的传出连接 列在 Google 的 ASN 15169 中。”

一旦我打开端口或尝试从另一个网络,它就可以工作。

【讨论】:

【参考方案3】:

SERVICE_NOT_AVAILABLE 可能是由旧的或缺少的 GooglePlayServices 引起的。 使用 GooglePlayServicesUtil 检查版本,如果错误重定向到市场,以便用户手动安装它 - 这将修复 SERVICE_NOT_AVAILABLE 并获得所有改进 和错误修复(和新错误:-)。

如果你不能这样做 - 使用 register() 没有意义,该方法期望匹配的播放服务检索结果。它确实会生成相同的广播,以实现向后兼容性。直接使用 REGISTER 意图或已弃用的库将继续工作 - 我建议使用该意图,GCMRegistrar 与 GoogleServicesFramework 中的旧实现相关联(它调用了 intent.setPackage(GSF_PACKAGE)),因此它无法受益从任何修复。

拥有最新版本的 GCM - 通过使用 GooglePlayServicesUtil 处理未自动安装的设备 - 不仅有助于注册。

【讨论】:

我认为这是真的。与往常一样,由于 Google Play 没有正确进行搜索,所以有些混乱,但经过一番摆弄后,Google Play 工作了,现在 GCM 工作了。我认为是 Google Play 服务。【参考方案4】:

阅读marnanish的帖子后,我尝试了下面的代码,我已经成功让我的Activity收到了注册。只需添加以下内容:

<activity
    android:name=".MyActivity"
    ... />
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
        <category android:name="your.package.here" />
    </intent-filter>
</activity>

在您的 AndroidManifest.xml 中

【讨论】:

【参考方案5】:

目前,在 Google 修复此错误或有人提供更好的解决方案之前,我正在使用混合解决方案,它会在前几次使用 Google Player Services(具有指数退避),但之后切换到 GCMRegistrar。

【讨论】:

【参考方案6】:

所以,还有一件事让我绊倒了。 我让它在模拟器 + 测试应用程序中运行良好,但在实际设备上部署它时遇到问题。 一直收到 SERVICE_NOT_AVAILABLE 错误,并采用类似于上述答案的方法:

     IntentFilter filter = new IntentFilter("com.google.android.c2dm.intent.REGISTRATION");
     BroadcastReceiver receiver = new BroadcastReceiver() 
         @Override
         public void onReceive(Context context, Intent intent) 
             Log.i(TAG, "Got into onReceive for manual GCM retrieval. "
             + intent.getExtras());
             String regId = intent.getStringExtra("registration_id");

             if (regId != null && !regId.isEmpty()) 
                 theResult.add(regId);
              else 
                 theResult.add("");
             
             lastDitchEffortComplete = true;
             
         ;
     cont.registerReceiver(receiver, filter);

这种方法奏效了。 但是接收这些信息需要花费很长的时间,而且在我的等待循环中,它只会在我放弃并让事情继续进行之后才能检索到它。

这让我想到 SERVICE_NOT_AVAILABLE 消息可能还有其他原因。

这是我必须使用 GCM 进行获取/注册的代码:

new AsyncTask<Void, Void, Void>() 
                final long timeSleep = 1000;

                @Override
                protected Void doInBackground(Void... params) 
                    GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(cont);
                    String registrationId = "";
                    int count = 0;
                    while (count < 1) 
                        try 
                            count++;
                            Log.i(TAG, "Starting GCM Registration Attempt #" + count);
                            registrationId = gcm.register(CLOUDAPPID);
                            rph.storeRegistrationId(registrationId);
                            return null;
                         catch (IOException e) 
                            e.printStackTrace();
                        

                        Log.w(TAG, "Registration failed.  Retrying in " + timeSleep + " ms.");
                        try 
                            Thread.sleep(timeSleep);
                         catch (InterruptedException e) 
                        
                    

                    return null;
                
            .execute().get();

这里要查找的主要内容是最后一行。

.execute.get();

一种“聪明/作弊”的方式来阻止异步离线结果。

事实证明,这就是导致问题的原因。 只要我有:

.execute();

让它通过,让算法/代码与报告一起工作,并且不需要立即注册 id,那就没问题了。我什至不需要手动广播接收器,它就可以正常工作。

【讨论】:

另外,这里有一个有趣的可能相关的问题/解决方案:***.com/questions/7109240/…【参考方案7】:

如果标记的答案对您不起作用(就像对我一样),请检查您设备前面的人,也许摇晃它,然后启用互联网连接。为我工作;-)

没有互联网,你会收到同样的异常,我花了一个下午的时间来解决服务的复杂问题....

【讨论】:

【参考方案8】:

我遇到了同样的问题,但它在所有设备上都发生了。我花了很长时间才找到我的问题,但我想我会发布它以防它与其他人的情况相匹配。这是我的问题的简化版本:

public class Login extends Activity 
    Context context;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        context = getApplicationContext();

        //Get GCM registration id
        new GcmRegistrationAsyncTask().execute(context);

        Set<Tuple> timeline = new HashSet<Tuple>();
        try 
            //Get data from Redis database
            timeline = new RedisAsyncTask().execute(context).get();
         catch (InterruptedException e) 
            e.printStackTrace();
         catch (ExecutionException e) 
            e.printStackTrace();
        
    

当我使用 get() 添加带有返回的第二个 AsyncTask 时,我每次都会得到 GCM 'SERVICE_NOT_AVAILABLE',即使我仍然可以从 GcmBroadcastReceiver onReceive() 检索注册 ID。但是,当我运行时:

public class Login extends Activity 
    Context context;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        context = getApplicationContext();

        new GcmRegistrationAsyncTask().execute(context);
        new RedisAsyncTask().execute(context);
    

GCM 将毫无问题地收到注册 ID。

【讨论】:

【参考方案9】:

我知道这是一篇旧帖子,但我遇到了同样的问题,但一切都设置正确。 SERVICE_NOT_AVAILABLE 错误,不断弹出。转到设置 => 应用程序 => Google Play 服务后,清除缓存并删除数据,它工作了。

【讨论】:

就我而言,这会将错误从SERVICE_NOT_AVAILABLE 更改为TIME_OUT

以上是关于Android 2.2 上的 GCM SERVICE_NOT_AVAILABLE的主要内容,如果未能解决你的问题,请参考以下文章

GCM 不适用于 Android 4.2.2?

Android TV 上的 GCM

在 Android 上的代理后面接收 FCM (GCM)

Android ICS 上的 GCM 通知不起作用

Linphone如何支持Android / iOS上的推送通知

如果在同一活动中,GCM 通知处理和避免活动打开