- PVSM.RU - https://www.pvsm.ru -
Верно, писали. Буквально на этой неделе на Хабре была опубликована статья GCM – новый сервис Push-уведомлений от Google [1] (если вы еще не знакомы с Google Cloud Messaging for Android, то советую прочитать её перед прочтением этой статьи, тем более в моей статье не описываются процесс создания проекта с GCM). Не знаю использовал её автор GCM в реальном приложении или нет, а вот мне пришлось. Поэтому-то я и хочу описать кое-что, чему не нашлось места в предыдущей статье, или что не было объяснено. Добавить это все комментарием в предыдущую статью, боюсь, невыполнимая задача.
<uses-permission android:name="android.permission.INTERNET" />
Тут всё ясно, без доступа к интернету GCM нам и не нужен
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
GCM требует доступ к Google-аккаунту
<uses-permission android:name="android.permission.WAKE_LOCK"/>
По этому поводу в прошлой теме даже был спор, но никто из участников не решил посмотреть в исходных код. Документация этот момент умалчивает, и лишь говорит, что возможно вы захотите захватить PowerManager.WakeLock. Так вот, если вы пользуетесь стандартной библиотекой GCM, то вам придется добавлять такое разрешение.
Вкратце механизм работы такой: наше приложение подписывается на получение широковещательных запросов. При получении запроса мы устанавливаем полученному Intent'у имя класса (setClassName()) в имя нашего сервиса расширяющего GCMBaseIntentService, затем захватываем WakeLock с флагом PowerManager.PARTIAL_WAKE_LOCK (не даем уснуть только CPU, экран и прочее спит спокойно), запускаем Intent как сервис, по выходу из onHandleIntent сервиса освобождаем WakeLock.
Не поверили и не стали добавлять это разрешение, и в итоге получаем вот такое исключение:
java.lang.SecurityException: Neither user 10110 nor current process has android.permission.WAKE_LOCK.
<permission
android:name="{имя пакета приложения}.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission
android:name="{имя пакета приложения}.permission.C2D_MESSAGE" />
Создаем свое собственное разрешение и сами его запрашиваем. Это мы делаем для того, чтобы никто кроме нас не смог получать наши сообщения.
Примечание: если вы выставили minSdkVersion в 16 или выше (Jelly Bean и последующие версии), то это разрешение вам не нужно (года через 2, надеюсь, можно будет опускать).
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
Собственно разрешение на регистрацию в GCM и получение сообщений.
Рассмотрим код из приложения-примера:
final String regId = GCMRegistrar.getRegistrationId(this);
if (regId.equals("")) {
// Automatically registers application on startup.
GCMRegistrar.register(this, SENDER_ID);
}
Вроде бы других условий нет. Так что, не изменяется? Если перейти по этой ссылке: http://developer.android.com/intl/ru/guide/google/gcm/adv.html#reg-state [2], можно узнать что все-таки может измениться. Таких случая два:
Для проверки на обновление программы я написал небольшой класс-помощник. Может быть кому-нибудь пригодится:
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.preference.PreferenceManager;
public final class ApplicationVersionHelper
{
public static final APP_VERSION_PREFS = "application_version";
public static boolean isApplicationVersionCodeEqualsSavedApplicationVersionCode(Context context)
{
return getApplicationVersionCode(context) == getApplicationVersionCodeFromPreferences(context);
}
public static int getApplicationVersionCode(Context context)
{
PackageManager pm = context.getPackageManager();
PackageInfo packageInfo;
int applicationVersion = 1;
try
{
packageInfo = pm.getPackageInfo(context.getPackageName(), 0);
applicationVersion = packageInfo.versionCode;
}
catch (NameNotFoundException ignored)
{
}
return applicationVersion;
}
public static int getApplicationVersionCodeFromPreferences(Context context)
{
return context.getSharedPreferences(APP_VERSION_PREFS, Context.MODE_PRIVATE).getInt("application_version_code", 0);
}
public static void putCurrentPackageVersionInPreferences(Context context)
{
context.getSharedPreferences(APP_VERSION_PREFS, Context.MODE_PRIVATE).edit().putInt("application_version_code", getPackageVersion(context)).commit();
}
}
Обратите внимание на то, что настройки получаются не через PreferenceManager.getDefaultSharedPreferences, а через именованный файл настроек. Для чего это делается, я объясню позже.
Теперь нам нужно вызвать putCurrentPackageVersionInPreferences после успешной регистрации в GCM и на нашем сервисе, а код проверки регистрации превращается в:
final String regId = GCMRegistrar.getRegistrationId(this);
if (regId.equals("") || !isApplicationVersionCodeEqualsSavedApplicationVersionCode(this)) {
// Automatically registers application on startup.
GCMRegistrar.register(this, SENDER_ID);
}
Для обработки создания резервной копии (не все об этой возможности вообще знают. Если стало интересно, то читать здесь — http://developer.android.com/intl/ru/guide/topics/data/backup.html [3]) я предлагаю следующее решение: просто не сохранять настройки с именем из константы ApplicationVersionHelper.APP_VERSION_PREFS при бэкапе. Вот и пригодился именованный файл настроек :) Тогда isApplicationVersionCodeEqualsSavedApplicationVersionCode вернет false при восстановлении данных и мы отправим запрос на регистрацию.
В GCMIntentService (классе унаследованном от GCMBaseIntentService)
нам предстоит переопределить несколько методов. Кратко по ним:
protected void onRegistered(Context context, String registrationId)
этот метод вызывается после успешной регистрации в GCM, отсюда нам нужно передать registrationId на наш сервер
protected void onUnregistered(Context context, String registrationId)
этот метод вызывается после успешной отмены регистрации в GCM, так же передаем registrationId на наш сервер для исключения из рассылки (многие приложения никогда не будут пользоваться данной возможностью)
protected void onMessage(Context context, Intent intent)
получение сообщения от GCM, если есть полезная нагрузка (payload), то данные лежат в intent
protected void onDeletedMessages(Context context, int total)
получение уведомления от GCM об удаленных сообщениях, что это такое и с чем их есть смотрите здесь — http://developer.android.com/intl/ru/guide/google/gcm/adv.html#payload [4]
public void onError(Context context, String errorId)
невосстановимая ошибка при получении данных, в errorId код ошибки
protected boolean onRecoverableError(Context context, String errorId)
восстановимая ошибка при получении данных, в errorId код ошибки. Если вернем true, то разрешим совершить еще одну попытку, если false, то прекратим попытки. Я рекомендую в этом методе возвращать super.onRecoverableError(context, errorId);
Не забудьте отменить процесс регистрации, если он запущен, и вызвать GCMRegistrar.onDestroy в методе onDestroy вашей главной Activity. Вот как это сделано у меня:
@Override
protected void onDestroy()
{
if (registerTask != null)
{
registerTask.cancel(true);
}
try
{
CMRegistrar.onDestroy(this);
}
catch(Exception ignored)
{
}
super.onDestroy();
}
registerTask тут — асинхронное задание (AsyncTask).
Советую прочитать http://developer.android.com/intl/ru/guide/google/gcm/index.html [5] (а там 5 пунктов) перед использованием GCM в своем приложении, а если есть вопросы (как насчет WAKE_LOCK разрешения), то не бояться залезть в исходных код.
Автор: 4ex
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/android/11366
Ссылки в тексте:
[1] GCM – новый сервис Push-уведомлений от Google: http://habrahabr.ru/post/147585/
[2] http://developer.android.com/intl/ru/guide/google/gcm/adv.html#reg-state: http://developer.android.com/intl/ru/guide/google/gcm/adv.html#reg-state
[3] http://developer.android.com/intl/ru/guide/topics/data/backup.html: http://developer.android.com/intl/ru/guide/topics/data/backup.html
[4] http://developer.android.com/intl/ru/guide/google/gcm/adv.html#payload: http://developer.android.com/intl/ru/guide/google/gcm/adv.html#payload
[5] http://developer.android.com/intl/ru/guide/google/gcm/index.html: http://developer.android.com/intl/ru/guide/google/gcm/index.html
Нажмите здесь для печати.