Обработка изменений идентификатора регистрации в Google Cloud Messaging на Android

В документах по облачным сообщениям Google в нем указано:

Приложение Android должно сохранить этот идентификатор для последующего использования (для экземпляр, чтобы проверить onCreate(), если он уже зарегистрирован). Заметка что Google может периодически обновлять идентификатор регистрации, поэтому вы должен проектировать ваше приложение для Android с пониманием того, что com.google.android.c2dm.intent.REGISTRATION можно называть много раз. Ваше Android-приложение должно быть в состоянии ответить соответственно.

Я зарегистрирую свое устройство, используя следующий код:

GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context);
String regID = gcm.register(senderID);

Класс GoogleCloudMessaging инкапсулирует процесс регистрации. Итак, как я могу обрабатывать com.google.android.c2dm.intent.REGISTRATION, поскольку обработка выполняется внутри класса GoogleCloudMessaging?

Ответ 1

Это интересный вопрос.

Google предлагает вам перейти на новый процесс регистрации:

Приложение Android, работающее на мобильном устройстве, регистрируется для приема сообщений, вызывая регистр метода GoogleCloudMessaging (senderID...). Этот метод регистрирует приложение для GCM и возвращает идентификатор регистрации. Этот оптимизированный подход заменяет предыдущий процесс регистрации GCM.

Заметка, в которой указано Google may periodically refresh the registration ID, появляется только на странице, которая все еще показывает старый процесс регистрации, поэтому возможно, что эта заметка больше не актуальна.

Если вы хотите быть в безопасности, вы все равно можете использовать старый процесс регистрации. Или вы можете использовать новый процесс, но в дополнение к этому код, который обрабатывает намерение com.google.android.c2dm.intent.REGISTRATION, чтобы убедиться, что вы охвачены, если Google решит обновить идентификатор регистрации.

Тем не менее, я никогда не испытывал такого обновления, и даже когда у меня возникли изменения в идентификаторе регистрации (обычно в результате отправки уведомления после снятия установки с приложения, а затем его повторной установки), старый идентификатор регистрации все еще работал (в результате был зарегистрирован идентификатор регистрации в Google), поэтому никакого вреда не было.

EDIT (06.06.2013):

Google изменил свое Demo App для использования нового интерфейса. Они обновляют идентификатор регистрации, устанавливая дату истечения срока действия, сохраненное локально приложением. Когда приложение запускается, они загружают свой локально сохраненный идентификатор регистрации. Если он "истек" (который в демо означает, что он был получен от GCM более 7 дней назад), они снова вызывают gcm.register(senderID).

Это не относится к гипотетическому сценарию, в котором идентификатор регистрации обновляется Google для приложения, которое не было запущено в течение длительного времени. В этом случае приложение не будет знать об изменении, и ни один из сторонних серверов не будет.

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);
    mDisplay = (TextView) findViewById(R.id.display);

    context = getApplicationContext();
    regid = getRegistrationId(context);

    if (regid.length() == 0) {
        registerBackground();
    }
    gcm = GoogleCloudMessaging.getInstance(this);
}

/**
 * Gets the current registration id for application on GCM service.
 * <p>
 * If result is empty, the registration has failed.
 *
 * @return registration id, or empty string if the registration is not
 *         complete.
 */
private String getRegistrationId(Context context) {
    final SharedPreferences prefs = getGCMPreferences(context);
    String registrationId = prefs.getString(PROPERTY_REG_ID, "");
    if (registrationId.length() == 0) {
        Log.v(TAG, "Registration not found.");
        return "";
    }
    // check if app was updated; if so, it must clear registration id to
    // avoid a race condition if GCM sends a message
    int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE);
    int currentVersion = getAppVersion(context);
    if (registeredVersion != currentVersion || isRegistrationExpired()) {
        Log.v(TAG, "App version changed or registration expired.");
        return "";
    }
    return registrationId;
}

/**
 * Checks if the registration has expired.
 *
 * <p>To avoid the scenario where the device sends the registration to the
 * server but the server loses it, the app developer may choose to re-register
 * after REGISTRATION_EXPIRY_TIME_MS.
 *
 * @return true if the registration has expired.
 */
private boolean isRegistrationExpired() {
    final SharedPreferences prefs = getGCMPreferences(context);
    // checks if the information is not stale
    long expirationTime =
            prefs.getLong(PROPERTY_ON_SERVER_EXPIRATION_TIME, -1);
    return System.currentTimeMillis() > expirationTime;
}

EDIT (08.14.2013):

Google снова изменил свое Demo App (два дня назад). На этот раз они удалили логику, в которой идентификатор регистрации истекает через 7 дней. Теперь они обновляют только регистрационный идентификатор, когда новая версия установленного приложения.

EDIT (04.24.2014):

Для полноты, вот слова Costin Manolache (взяты из здесь), разработчика Google, участвующего в разработке GCM, по этому вопросу:

"Периодическое обновление" никогда не происходило, и обновление регистрации не входит в новую библиотеку GCM.

Единственной известной причиной изменения идентификатора регистрации является старая ошибка приложений автоматически получать незарегистрированные сообщения, если они получают сообщение в то время как обновляется. Пока эта ошибка не будет устранена, приложениям все равно необходимо позвонить register() после обновления, и до сих пор идентификатор регистрации может изменяться в Это дело. Вызов unregister() явно обычно изменяет Идентификатор регистрации.

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

Это объясняет текущую реализацию официального демонстрационного приложения GCM. com.google.android.c2dm.intent.REGISTRATION никогда не должно обрабатываться при использовании класса GoogleCloudMessaging для регистрации.

Ответ 2

Чтение нового API-интерфейса InstanceID, я нашел больше информации о том, когда токен может измениться:

Ваше приложение может запрашивать токены из службы идентификатора экземпляра по мере необходимости используя метод getToken(), и подобно InstanceID, ваше приложение также может хранить токены на своем собственном сервере. Все токены, выпущенные для вашего приложения, принадлежат в приложение InstanceID.

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

Подробнее:

Служба идентификатора экземпляра периодически вызывает обратные вызовы (например, каждые 6 месяцев), запрашивая, чтобы ваше приложение обновляло свои жетоны. Это может также инициировать обратные вызовы, когда:

  • Есть проблемы с безопасностью; например, проблемы с SSL или платформой.
  • Информация об устройстве больше не действительна; например, резервное копирование и восстановление.
  • В противном случае будет задействована служба идентификатора экземпляра.

Источники:

https://developers.google.com/instance-id/

https://developers.google.com/instance-id/guides/android-implementation

Ответ 3

После очистки тонны вводящих в заблуждение ответов в сети, в том числе SO, единственное место, где я нашел полный ответ, было как указано в ответе Эрана и здесь:

В то время как автоматическое обновление регистрации могло или не могло произойти, google описывает простой алгоритм обработки canocical_ids путем анализа успешного ответа:

If the value of failure and canonical_ids is 0, it not necessary to parse the remainder of the response. Otherwise, we recommend that you iterate through the results field and do the following for each object in that list:

If message_id is set, check for registration_id:
If registration_id is set, replace the original ID with the new value (canonical ID) in your server database. Note that the original ID is not part of the result, so you need to obtain it from the list of code>registration_ids passed in the request (using the same index).
Otherwise, get the value of error:
If it is Unavailable, you could retry to send it in another request.
If it is NotRegistered, you should remove the registration ID from your server database because the application was uninstalled from the device or it does not have a broadcast receiver configured to receive com.google.android.c2dm.intent.RECEIVE intents.
Otherwise, there is something wrong in the registration ID passed in the request; it is probably a non-recoverable error that will also require removing the registration from the server database. See Interpreting an error response for all possible error values.

Из вышеупомянутой ссылки.