Пользователь
0,0
рейтинг
18 февраля 2014 в 16:48

Разработка → Обеспечиваем надежную работу Google Cloud Messaging

Целью статьи является ознакомление с наиболее распространенными подводными камнями в работе с сервисом нотификаций от Google.
Источником послужила очень полезная, на мой взгляд, статья Keeping Google Cloud Messaging For Android Working Reliably от разработчиков Pushbullet — удобного приложения для синхронизации нотификаций между Android устройствами и браузером Chrome.

Надеюсь все из вас уже ознакомлены со статьями по Google Cloud Messaging:


Урок №1: Будьте готовы часто получать SERVICE_NOT_AVAILABLE


Этот момент совершенно игнорируется в официальной документации Implementing GCM Client. Таким образом, это одна из важнейших деталей, которые необходимо знать при регистрации GCM.

Вызов gcm.register(SENDER_ID); очень часто приводит к ошибке с исключением IOException «SERVICE_NOT_AVAILABLE».

Есть вероятность, что она никогда не произойдет на ваших тестовых устройствах и можно оказаться не готовым к ней. Это может привести к очень негативному эффекту, так как надежность вашего приложения может серьёзно пострадать, в тот момент когда ваше приложение уже опубликовано, а так случается очень часто.

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

Урок №2: Будьте готовы к повторяющимся ошибкам вызова register, даже если рабочий registration ID создан


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

Ошибка заключается в следующем:
Совершенно не важно сколько раз вы вызывали gcm.register(SENDER_ID); он постоянно падает с ошибкой на некоторых устройствах. Даже если рабочий registration ID был успешно создан, но так и не был возвращен в результате функции.
Что бы получить этот registration ID, зарегистрируйте свой GCM BroadcastReceiver на следующее событие:
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />

Вы получите возможность получить рабочий registration ID:
final String registrationId = intent.getStringExtra("registration_id");

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

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

Урок №3: Убедитесь что вы регистрируете свое устройство заново всякий раз, когда обновляете своё приложение


Этот совет взят из официальной документации, но его очень легко пропустить, так что не забывайте о нем.

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

Лучший способ активировать автоматический процесс регистрации, это объявить в AndroidManifest класс BroadcastReceiver, который будет слушать событие PACKAGE_REPLACED:
<receiver android:name=".UpdateReceiver">
    <intent-filter>
        <action android:name="android.intent.action.PACKAGE_REPLACED" />
        <data android:path="<YOUR_PACKAGE_NAME_HERE>"
              android:scheme="package" />
    </intent-filter>
</receiver>

В тот момент, когда метод onReceive будет вызван, вы можете инициировать регистрацию GCM. Далее заменяете текущий registration ID на новый и отправляете его на свой сервер.

Надеюсь вы помните, что вызывать
gcm = GoogleCloudMessaging.getInstance(activity); 
regid = gcm.register(SENDER_ID);
непосредственно в onReceive BroadcastReceiver-а не стоит, так как этот метод будет вызван в главном потоке приложения (MAIN_THREAD), и не должен работать на прямую с сетью.
Для этих целей рекомендуется использовать AsyncTask, либо специально созданный фоновый сервис, либо Thread на худой конец.


Урок №4: Убедитесь что вы регистрируете свое устройство заново в случае обновления версии Android на устройстве


Это нигде не документировано (насколько я знаю), но известно, что registration ID зависит от Android ID устройства.
Который может измениться если меняется прошивка устройства или пользователь делает сброс к заводским установкам (factory reset). Таким образом надо зарегистрировать устройство заново если произошло изменение Android ID.

Так же как и в случае с обновлением приложения, пользователь вряд ли сразу же запустит ваше приложение, которое могло бы инициировать процесс регистрации GCM при запуске. Вы должны позаботится об автоматической перерегистрации устройства для того что бы обеспечить работу службы сообщений без ожидания запуска приложения пользователем.
Какой лучший способ сделать это? Существует единственный способ сделать это, хоть этот способ и не идеальный. Вам нужно регистрировать ваше приложение всякий раз, когда телефон перезагружается. Для этого надо дать приложению в AndroidManifest новое разрешение:
<uses-permission
    android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

И настроить BroadcastReceiver на приём события BOOT_COMPLETED:
<receiver android:name=".BootReceiver">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

Теперь, каждый раз при вызове onReceive этого слушателя вы сможете снова зарегистрировать ваше устройство в GCM.

Возможно, правильнее было бы вызывать регистрацию устройства только в случае, если Android ID действительно изменился. Важно то, что с помощью этой, казалось бы, не обязательной работы бы будем уверены что устройство зарегистрировано даже когда это возможно было не нужно, мы ведь не можем быть на 100% уверены, что Android ID обязательно изменится.

На этом все. Печально, что для того, что бы обеспечить бесперебойную работу GCM, пришлось идти на подобные «танцы с бубном» и еще печальней, что большинство этих моментов совершенно не документированы самим Google.

Ошибки и опечатки в личку
djvu @djvu
карма
16,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (4)

  • 0
    Урок №1: Будьте готовы часто получать SERVICE_NOT_AVAILABLE

    А что в данном случае возвращает GooglePlayServicesUtil.isGooglePlayServicesAvailable(context)?
    Может сервисы не доступны или не обновлены?

    Урок №2: Будьте готовы к повторяющимся ошибкам вызова register, даже если рабочий registration ID создан

    Вы о чем? Механизм с получением registartionID через broadcat использовался с с2dm, в GCM его возвращает сразу метод.

    или пользователь делает сброс к заводским установкам (factory reset)

    если пользователь делает factory reset ваше приложение удаляется, оО.

    На этом все. Печально, что для того, что бы обеспечить бесперебойную работу GCM, пришлось идти на подобные «танцы с бубном» и еще печальней, что большинство этих моментов совершенно не документированы самим Google.

    Если не читать документацию и делать абы как, то да действительно не будет работать.
    Если следовать документации, все прекрасно работает, как ни странно.
    • 0
      А что в данном случае возвращает GooglePlayServicesUtil.isGooglePlayServicesAvailable(context)?
      Может сервисы не доступны или не обновлены?
      Насколько я понимаю, этот метод проверяет наличие установленного в системе пакета Google Play Services и его версию, а не факт доступности подключения.
      Только что проверил его работу, отключив работу 3G и WiFi сразу перед вызовом isGooglePlayServicesAvailable — результат ConnectionResult.SUCCESS.

      Вы о чем? Механизм с получением registartionID через broadcat использовался с с2dm, в GCM его возвращает сразу метод.
      Большей частью эта ошибка преследует тех, кто до сих пор использует c2dm, тем не менее, согласно документации метод public String register (String… senderIds) throws IOException, таким образом в случае ошибок связи с гугловыми сервисами будет выбрасываться именно исключение, в статье же приводится альтернативная попытка получения registartion ID

      если пользователь делает factory reset ваше приложение удаляется, оО.
      Согласен, этот кейс тут приведен скорее как один из теоретических примеров, когда может измениться Android ID.
      Что не отменяет случая с обновлением прошивки телефона без потери установленного софта. Изменил формулировку в статье.
      • 0
        Насколько я понимаю, этот метод проверяет наличие установленного в системе пакета Google Play Services и его версию, а не факт доступности подключения.
        Только что проверил его работу, отключив работу 3G и WiFi сразу перед вызовом isGooglePlayServicesAvailable — результат ConnectionResult.SUCCESS.

        Этот метод проверяет не только установлены или нет, а также актуальная ли версия, не отключены ли сервисы в системе. Если статус отличный от ConnectionResult.SUCCESS, то вы однозначно не сможете воспользоватся пушами.

        Большей частью эта ошибка преследует тех, кто до сих пор использует c2dm

        А зачем использовать c2dm, если он уже немалое время deprecated? Понятное дело, что вы с ним будете испытывать проблемы, не зря же его перестали поддерживать.
        Во вторых, вы пытаетесь ввести читателя в заблуждение, вначале говорите о GCM, а потом плавно на c2dm переходите и говорите какая плохая документация для GCM, ничего не работает.

        тем не менее, согласно документации метод public String register (String… senderIds) throws IOException, таким образом в случае ошибок связи с гугловыми сервисами будет выбрасываться именно исключение, в статье же приводится альтернативная попытка получения registartion ID

        Понятное дело, что может возникнуть, если у вас нет конешина к интернету. Этот кейс прекрасно описан в ссылке, которую вы приводили:
        } catch (IOException ex) {
              msg = "Error :" + ex.getMessage();
             // If there is an error, don't just keep trying to register.
             // Require the user to click a button again, or perform
             // exponential back-off.
        }
        


        И с GCM вам никак не может слушание несуществующего броадкаста. В чем вина гугла, если вы не обрабатываете ошибку?
  • 0
    Я думал статья о том как обеспечить надежную доставку GCM :(
    В этом основная проблема — подвисание сообщений при GPRS соединении

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.