16

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

我正在使用新的 Google Play 服务编写一个使用 GoogleCloudMessaging 的应用程序。我使用 Android 网站上提供的指南实现了它,我的源代码包含许多错误检查和处理代码,例如确保安装或更新 Google Play 服务。GCM 注册代码还按照谷歌的建议实现了指数退避,以实现处理 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,它最终在设备上工作。但是我几乎不认为这是解决问题的好方法,因为谷歌已经停止支持这种方法 afaik。

EDIT2:查看接受的答案。

4

9 回答 9

52

我遇到了同样的问题。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,并且可以向我的智能手机发送消息。很奇怪,但它对我有用。

于 2013-07-18T10:48:55.077 回答
5

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

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

一旦我打开端口或尝试从另一个网络,它就起作用了。

于 2014-01-20T15:18:33.997 回答
3

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

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

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

于 2013-07-27T15:59:20.077 回答
2

在阅读了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

于 2013-10-04T07:49:54.447 回答
0

For the time being, until either Google fix this bug or someone provides a better solution I am using a hybrid solution where it will use Google Player Services for the first few times (with exponential backoff) but switch to GCMRegistrar afterwards.

于 2013-07-15T20:51:07.567 回答
0

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

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。

于 2014-07-23T01:53:06.647 回答
0

所以,还有一件事要注意,这让我绊倒了。我让它在模拟器+测试应用程序中运行良好,但后来我在实际设备上部署它时遇到了问题。
一直收到 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,那就没问题了。我什至不需要手动广播接收器,它就像它应该的那样工作。

于 2013-10-18T18:55:09.510 回答
0

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

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

于 2014-01-07T14:11:26.627 回答
0

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

于 2016-06-13T10:01:21.923 回答