вторник, 30 июня 2015 г.

Android GCM. Часть 1. Регистрация устройства.

Ни одно современное мобильное приложение сейчас не обходится без пуш уведомлений - сообщений, рассылаемых сервером на устройства клиентов. Google предоставляет сервис Cloud Messaging (GCM) для рассылки уведомлений на устройства Android.

Здесь будет рассказано, как интегрировать поддержку GCM в свое приложение.
(Реализация на серверной стороне будет рассмотрена отдельно)

Архитектура


Реализация GCM включает в себя 3 составляющих:
1. Сервер приложения, с которого исходят уведомления;
2. Мобильное устройство, которое принимает уведомления;
3. GCM серверы, которые отправляют сообщения с сервера приложения на мобильные устройства.


Перед началом работы


Для разработки понадобиться:
1. Установить Google Play services SDK, если он еще не установлен у вас. Инструкция по установке здесь.
2. Подключить Google Play Services для GCM в приложении, добавив строчку compile 'com.google.android.gms:play-services-gcm:7.5.0' в Gradle. Так же убедитесь, что все ссылки на Google Play Services в Gradle имеют одинаковую версию, иначе вы не сможете запустить отладку. Инструкция здесь.
3. Создать приложение в Google Developer Console и активировать в нем GCM API, как описано здесь. Это забота серверной стороны. Клиентскому приложению нужен только номер созданного приложения из Google Developer Console. Если вы создаете только клиентское приложение, уточните этот номер у разработчика серверной части.

Редактирование файла Manifest


Добавьте следующие строчки в manifest приложения:

1. <uses-permission android:name="android.permission.INTERNET" />
Для того, чтобы приложение могло отправить идентификатор устройства на сервер.

2. <uses-permission android:name="android.permission.WAKE_LOCK" />
Предотвращает засыпание процессора во время приема сообщений.

3. <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
Для разрешения приложению регистрировать и принимать сообщения

4. <permission android:name="com.example.gcm.permission.C2D_MESSAGE"
       android:protectionLevel="signature" />
<uses-permission android:name="com.example.gcm.permission.C2D_MESSAGE" />
Нужен для запрета другим приложениям принимать уведомления создаваемого приложения.
Здесь нужно заменить com.example.gcm на имя пакета вашего приложения.

5. <uses-permission android:name="android.permission.GET_ACCOUNTS" />
Было в описании, но не сказано, для чего нужно.

Полная инструкция здесь.

Регистрация устройства


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

Токен будем хранить в Shared Preferences. Так же нужно хранить информацию о том, отправлен токен на сервер или нет. Сначала напишем методы для этого:

// Имя Shared Preferences
private static final String SHARED_PREF_NAME = "Preferences";
// Ключ токена
private static final String PREF_DEVICE_TOKEN = "DeviceToken";
// Флаг, отвечающий за то, отправлен токен на сервер или нет
private static final String PREF_DEVICE_TOKEN_SENT = "DeviceTokenSent";

// Возвращает Shared Preferences

public static SharedPreferences getSharedPreferences(Context context) {
    return context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
}

// Сохраняет токен в Shared Preferences

public static void setDeviceToken(Context context, String deviceToken) {
    SharedPreferences sp = getSharedPreferences(context);
    SharedPreferences.Editor editor = sp.edit();
    editor.putString(PREF_DEVICE_TOKEN, deviceToken);
    editor.commit();
}

// Достает токен из Shared Preferences

public static String getDeviceToken(Context context) {
    SharedPreferences sp = getSharedPreferences(context);
    return sp.getString(PREF_DEVICE_TOKEN, null);
}

// Возвращает true, если токен отправлен на сервер

public static boolean isDeviceTokenSent(Context context) {
    SharedPreferences sp = getSharedPreferences(context);
    return sp.getBoolean(PREF_DEVICE_TOKEN_SENT, false);
}

// Задает флаг, отправлен токен на сервер или нет

public static void setDeviceTokenSend(Context context, boolean sent) {
    SharedPreferences sp = getSharedPreferences(context);
    SharedPreferences.Editor editor = sp.edit();
    editor.putBoolean(PREF_DEVICE_TOKEN_SENT, sent);
    editor.commit();
}

Теперь пишем метод для создания и сохранения токена устройства. Для получения токена на мобильном устройстве используется Instance ID API:

// Номер проекта в Google Developer Console
public final static String PROJECT_NUMBER = "111233456789";
// Пространство для получение токена. Равно строке "GCM"
public final static String SCOPE = GoogleCloudMessaging.INSTANCE_ID_SCOPE;

// Возвращает токен устройства.

// Если его еще не существует, то создает его и сохраняет в Shared Preferences
public static String createDeviceToken(Context context) {
    String token = getDeviceToken(context);
    if(token == null) {
        token = InstanceID.getInstance(context).getToken(PROJECT_NUMBER, SCOPE, null);
        setDeviceToken(context, token);
    }
    return token;
}

Номер проекта можно найти во вкладке Overview:



Метод getToken в Instance ID API является долгой операцией, следовательно createDeviceToken должен быть выполнен в отдельном потоке.

// Отправляет токен на сервер.
// Если токена не существует, создает его.
public static void createAndSendDeviceToken(final Context context) {
    // Если токен уже отправлен на сервер, то ничего не делаем
    if(isDeviceTokenSent(context)) {
        return;
    }

    new AsyncTask<Void, Void, String>() {

        @Override
        protected String doInBackground(Void... params) {
            try {
                return createDeviceToken(context)
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
        }

        @Override

        protected void onPostExecute(String s) {
            super.onPostExecute(s);

            if (s != null) {

                sendTokenToServer(s);
            } else {
                // Действие при ошибке
            }
        }
    }.execute();
}

Осталось реализовать отправку токена на сервер в методе sendTokenToServer. Если токен успешно отправлен, запомните это вызовом метода setDeviceTokenSend(context, true);

5 комментариев:

  1. Номер проекта в Google Developer Console не нашел.

    ОтветитьУдалить
    Ответы
    1. Посмотрите во вкладке Overview вверху страницы. Поправил статью, добавил скрин.

      Удалить
  2. Этот комментарий был удален автором.

    ОтветитьУдалить
  3. Скорее всего нет. Первую часть я писал потому, что официальная документация по части получения токена устройства была разрознена. Темами второй части планировались прием и показ уведомлений. Документация хорошо описывает это.
    Так же, сейчас я готовлю большой материал по разработке под Android в целом, так что заострять внимание конкретно на пуш уведомления не планирую.

    ОтветитьУдалить