GCM SERVICE_NOT_AVAILABLE на Android 2.2

Я получаю сообщение об ошибке "SERVICE_NOT_AVAILABLE" при вызове GoogleCloudMessaging.register() на устройстве Android 2.2.

Я пишу приложение, которое использует GoogleCloudMessaging с использованием новых сервисов Google Play. Я реализовал это, используя рекомендации, представленные на веб-сайте Android, и мой источник содержит много кода для проверки ошибок и обработки, например, для проверки того, что Сервисы Google Play установлены или обновлены. В регистрационном коде GCM также реализован экспоненциальный откат в Google, как это было предложено для обработки ошибки SERVICE_NOT_AVAILABLE.

Я протестировал свое приложение на множестве устройств, включая ICS, JB, Honey Comb и даже устройства 2.3.x. Регистрация в GCM работает, и я могу отправлять ей сообщения через GCM. Однако на устройстве 2.2 я постоянно получаю ошибку SERVICE_NOT_AVAILABLE при вызове GoogleCloudMessaging.register() даже при экспоненциальном откате.

Устройство, на котором происходит сбой GCM, является Samsung SGH-I896, если быть точным, и на телефоне установлено 2 аккаунта Google. Я читал, что эта ошибка может быть вызвана неправильно настроенным временем, но время установлено на автоматическое. Телефон не смоделирован и работает под управлением самсунг стокового ПЗУ.

Я также попытался перезагрузить устройство и переустановить Сервисы Google Play без удачи. Любая помощь по этому вопросу будет принята с благодарностью.

РЕДАКТИРОВАТЬ: Я попытался реализовать GCM, используя старый gcm.jar и GCMRegistrar, и он закончил работать на устройстве. Однако я вряд ли считаю это хорошим решением проблемы, так как Google прекратил поддерживать этот метод.

EDIT2: см. Принятый ответ.

9 ответов

Решение

Я испытал ту же проблему. GCM отлично работает на моем планшете под управлением Android 4.04, но всегда получал SERVICE_NOT_AVAILABLE на моем смартфоне под управлением Android 2.3.

Я нашел следующий обходной путь, не используя (насколько я знаю) устаревшие классы. Добавьте действие "com.google.android.c2dm.intent.REGISTRATION" к GCMBroadcastReceiver в своем манифесте. Это позволит получить регистрационный идентификатор на вашем GCMBroadcastReceiver.

<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 может получить регистрационный идентификатор:

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 и могу отправлять сообщения на свой смартфон. Довольно странно, но у меня это работает.

Эта ошибка также произошла со мной, но это произошло потому, что я пытался зарегистрироваться в GCM через брандмауэр моей компании. И в документации я нашел:

"Примечание. Если в вашей организации есть брандмауэр, который ограничивает трафик в Интернет или из Интернета, его необходимо настроить, чтобы разрешить подключение к GCM, чтобы устройства Android могли получать сообщения. Открываемые порты: 5228, 5229 и 5230. GCM обычно использует только 5228, но иногда использует 5229 и 5230. GCM не предоставляет конкретные IP-адреса, поэтому вы должны разрешить брандмауэру принимать исходящие подключения ко всем IP-адресам, содержащимся в блоках IP, перечисленных в ASN Google 15169."

Как только я открыл порты или попробовал из другой сети, это работало.

SERVICE_NOT_AVAILABLE может быть вызвано старым или отсутствующим GooglePlayServices. Используйте GooglePlayServicesUtil, чтобы проверить версию и, если неправильно, перенаправить на рынок, чтобы пользователь мог установить ее вручную - это исправит SERVICE_NOT_AVAILABLE, а также получит все улучшения и исправления ошибок (и новые ошибки:-).

Если вы не можете сделать это - нет смысла использовать register(), метод ожидает, что соответствующий сервис воспроизведения получит результат. Он генерирует ту же самую трансляцию для обратной совместимости. Использование намерения REGISTER напрямую или устаревшая библиотека продолжит работать - я бы предложил использовать намерение, GCMRegistrar привязан к старой реализации в GoogleServicesFramework (у него есть вызов intent.setPackage(GSF_PACKAGE)), поэтому он не может извлечь выгоду от любых исправлений.

Наличие последней версии GCM - с помощью GooglePlayServicesUtil для работы с устройствами, которые не получили автоматическую установку - может помочь не только для регистрации.

После прочтения поста от marnaish, я попробовал код ниже, и я успешно получил свою активность, чтобы получить регистрацию. Просто добавьте это:

<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

Если отмеченный ответ не работает для вас (как это было для меня), проверьте человека перед вашим устройством, возможно встряхните его и затем включите интернет-соединение. Работал на меня;-)

Без интернета вы получаете то же исключение, и я провел весь день в погоне за сложными проблемами с сервисом....

Я знаю, что это старый пост, но у меня была та же проблема, когда все было настроено правильно. Ошибка SERVICE_NOT_AVAILABLE, продолжал появляться. Перейдя в настройки => apps => Google Play-сервисы, очистив кеш и удалив данные, все заработало.

Итак, есть еще одна вещь, чтобы следить за тем, что сбило меня с толку. У меня все работало нормально в эмуляторе + с тестовым приложением, но потом возникли проблемы с его развертыванием на реальных устройствах.
Последовательно получал ошибку 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();

Позвольте этому пройти и заставили алгоритм / код работать с отчетностью и без необходимости регистрации идентификатора сразу, тогда это было хорошо. Мне даже не нужен был ручной приемник вещания, он просто работал как надо.

В настоящее время, пока Google не исправит эту ошибку или кто-то не предоставит лучшее решение, я использую гибридное решение, в котором он будет использовать Сервисы Google Player в первые несколько раз (с экспоненциальным откатом), но впоследствии переключится на GCMRegistrar.

У меня была такая же проблема, но это происходило на всех устройствах. У меня ушло навсегда, чтобы найти свою проблему, но я подумал, что опубликую ее, если она соответствует чьему-либо делу Вот упрощенная версия моей проблемы:

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();
        }
    }
}

Когда я добавляю второй AsyncTask с возвратом, используя get(), я получаю GCM 'SERVICE_NOT_AVAILABLE' каждый раз, даже если я все еще могу получить регистрационный идентификатор из GcmBroadcastReceiver onReceive(). Тем не менее, когда я бегу:

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 получит регистрационный идентификатор без проблем.