Продвинутый Android на Kotlin 01.2: Android Firebase Cloud Messaging

Эта практическая работа входит в курс «Advanced Android in Kotlin». Вы получите максимальную пользу от этого курса, выполняя задания последовательно, но это не обязательно. Все практическая работа курса перечислены на целевой странице практической работы «Advanced Android in Kotlin» .

Введение

В предыдущей лабораторной работе вы добавили уведомления к таймеру для варки яиц, которые создаются и активируются внутри вашего приложения. Другой важный вариант использования уведомлений — удалённая отправка push-уведомлений, которые можно получать, даже когда приложение не запущено.

Что такое push-уведомление?

Push-уведомления — это уведомления, которые сервер «отправляет» на мобильные устройства. Они могут быть доставлены на устройство независимо от того, запущено ли ваше приложение или нет.

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

Push-уведомления используют модель публикации/подписки , которая позволяет бэкенд-приложениям отправлять релевантный контент заинтересованным клиентам. Без модели публикации/подписки пользователям вашего приложения пришлось бы периодически проверять наличие обновлений. Этот процесс утомителен и ненадёжен для пользователей. Более того, по мере роста числа клиентов эти периодические проверки создадут слишком большую нагрузку на сетевые и вычислительные ресурсы как для сервера вашего приложения, так и для устройства пользователя.

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

Что такое Firebase Cloud Messaging?

Firebase Cloud Messaging — часть платформы Firebase для разработки мобильных приложений. Обычно для этого требуется настроить сервер с нуля, который будет взаимодействовать с мобильными устройствами для запуска уведомлений. С Firebase Cloud Messaging вы можете отправлять уведомления всем пользователям установленного приложения или их подгруппе без настройки сервера. Например, вы можете отправить пользователям напоминание или предложить им специальную акцию, например, бесплатный подарок. Вы можете удалённо отправлять уведомления на одно или несколько устройств.

Вы также можете использовать Firebase Cloud Messages для передачи данных из вашего внутреннего приложения или из проекта Firebase вашим пользователям.

В этой лабораторной работе вы узнаете, как использовать Firebase Cloud Messaging для отправки push-уведомлений для вашего Android-приложения, а также для отправки данных.

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

Что вам уже следует знать

Вам должно быть знакомо:

  • Как создавать Android-приложения на Kotlin. В частности, работа с Android SDK.
  • Как разработать приложение с использованием компонентов архитектуры и привязки данных.
  • Базовые знания о вещательных приемниках.
  • Базовое понимание AlarmManager.
  • Как создавать и отправлять уведомления с помощью NotificationManager.

Чему вы научитесь

  • Как отправлять сообщения пользователю через Firebase Cloud Messaging.
  • Как отправлять данные из бэкэнда в ваше приложение с помощью сообщений с данными, которые являются частью Firebase Cloud Messaging.

Что ты будешь делать?

  • Добавьте push-уведомления в стартовое приложение.
  • Управляйте облачными сообщениями Firebase во время работы вашего приложения.
  • Передача данных с помощью Firebase Cloud Messaging.

В этой лабораторной работе вы будете работать с кодом из предыдущей работы «Использование уведомлений в приложениях Android» . В предыдущей работе вы создали приложение-таймер для варки яиц, которое отправляет уведомления по истечении времени приготовления. В этой работе вы добавите Firebase Cloud Messaging для отправки push-уведомлений пользователям вашего приложения, чтобы напомнить им о необходимости съесть яйца.

Чтобы получить образец приложения, вы можете:

Клонируйте репозиторий с GitHub и переключитесь на стартовую ветку:

$  git clone https://github.com/googlecodelabs/android-kotlin-notifications-fcm


Кроме того, вы можете загрузить репозиторий в виде ZIP-файла, распаковать его и открыть в Android Studio.

Загрузить ZIP-архив

Шаг 1: Создайте проект Firebase

Прежде чем добавить Firebase в свое приложение Android, вам необходимо создать проект Firebase для подключения к вашему приложению Android.

  1. Войдите в консоль Firebase .
  2. Нажмите «Добавить проект» , затем выберите или введите имя проекта . Назовите свой проект fcm-codelab .
  3. Нажмите «Продолжить» .
  4. Вы можете пропустить настройку Google Analytics, отключив кнопку Включить Google Analytics для этого проекта .
  5. Нажмите «Создать проект» , чтобы завершить настройку проекта Firebase.

Шаг 2: Зарегистрируйте свое приложение в Firebase

Теперь, когда у вас есть проект Firebase, вы можете добавить в него свое приложение для Android.

  1. В центре страницы обзора проекта консоли Firebase щелкните значок Android , чтобы запустить рабочий процесс настройки.

  1. В поле Имя пакета Android введите com.example.android.eggtimernotifications .
  2. Нажмите «Зарегистрировать приложение» .

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

Шаг 3: Добавьте файл конфигурации Firebase в свой проект

Добавьте файл конфигурации Firebase Android в свое приложение.

  1. Нажмите «Загрузить google-services.json» , чтобы получить файл конфигурации Firebase для Android ( google-services.json ). Убедитесь, что имя файла конфигурации не дополнено дополнительными символами и имеет точное имя google-services.json .
  2. Переместите файл конфигурации в каталог модуля (уровня приложения) вашего приложения.

Шаг 4: Настройте свой проект Android для включения продуктов Firebase

Чтобы включить продукты Firebase в вашем приложении, необходимо добавить плагин google-services в файлы Gradle.

  1. В корневом файле Gradle (уровня проекта) ( build.gradle ) проверьте наличие репозитория Maven от Google.
  2. Затем добавьте правила для включения плагина Google Services.

build.gradle

buildscript {

  repositories {
    // Check that you have the following line (if not, add it):
    google()  // Google's Maven repository
  }

  dependencies {
    // ...

    // Add the following line:
    classpath 'com.google.gms:google-services:4.3.2'  // Google Services plugin
  }
}

allprojects {
  // ...

  repositories {
    // Check that you have the following line (if not, add it):
    google()  // Google's Maven repository
    // ...
  }
}
  1. В файле Gradle вашего модуля (уровня приложения) (обычно app/build.gradle ) добавьте строку для применения плагина в конец файла.

приложение/build.gradle

apply plugin: 'com.android.application'

android {
  // ...
}

// Add the following line to the bottom of the file:
apply plugin: 'com.google.gms.google-services'  // Google Play services Gradle plugin

В этом задании вы добавите Firebase Cloud Messaging (FCM) в свой проект для использования push-уведомлений.

Код сервиса Android для FCM в этой лабораторной работе представлен в файле MyFirebaseMessagingService.kt . Далее вы добавите код в своё приложение Android.

Для тестирования вашей реализации вы будете использовать компоновщик уведомлений . Компоновщик уведомлений — это инструмент, который поможет вам составлять и отправлять сообщения с веб-сайта консоли Firebase.

  1. Откройте MyFirebaseMessagingService.kt
  2. Проверьте файл и, в частности, следующие функции:
  • onNewToken() — вызывается автоматически, если ваша служба зарегистрирована в манифесте Android. Эта функция вызывается при первом запуске приложения и каждый раз, когда Firebase выпускает новый токен для вашего приложения. Токен — это ключ доступа к вашему бэкенд-проекту Firebase. Он генерируется для вашего конкретного клиентского устройства. С помощью этого токена Firebase определяет, какому клиенту бэкенд должен отправлять сообщения. Firebase также определяет, является ли этот клиент допустимым и имеет ли он доступ к данному проекту Firebase.
  • onMessageReceived — вызывается, когда приложение запущено и Firebase отправляет ему сообщение. Эта функция получает объект RemoteMessage , который может содержать уведомление или сообщение с данными. Подробнее о различиях между уведомлениями и сообщениями с данными вы узнаете далее в этой практической работе.

Шаг 1: Отправка уведомлений FCM на одно устройство

Консоль уведомлений позволяет протестировать отправку уведомления. Чтобы отправить сообщение на определённое устройство с помощью консоли, необходимо знать токен регистрации этого устройства.

Когда бэкенд Firebase генерирует новый или обновляет токен, вызывается функция onNewToken() с новым токеном в качестве аргумента. Если вы хотите настроить рассылку на одно устройство или создать группу устройств, вам потребуется получить доступ к этому токену, расширив FirebaseMessagingService и переопределив onNewToken() .

  1. Откройте AndroidManifest.xml и раскомментируйте следующий код, чтобы включить службу MyFirebaseMessagingService для приложения-таймера для варки яиц. Метаданные службы в манифесте Android регистрируют MyFirebaseMessagingService как службу и добавляют фильтр намерений, чтобы эта служба получала сообщения от FCM. В последней части метаданных breakfast_notification_channel_id объявляется как default_notification_channel_id для Firebase. Этот идентификатор понадобится вам на следующем шаге.
<!-- AndroidManifest.xml -->
<!-- TODO: Step 3.0 uncomment to start the service  -->

        <service
                android:name=".MyFirebaseMessagingService"
                android:exported="false">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT"/>
            </intent-filter>
        </service>
        <!-- [START fcm_default_icon] -->
        <!--
 Set custom default icon. This is used when no icon is set for incoming notification messages.
             See README(https://goo.gl/l4GJaQ) for more.
        -->
        <meta-data
                android:name="com.google.firebase.messaging.default_notification_icon"
                android:resource="@drawable/common_google_signin_btn_icon_dark"/>
        <!--
 Set color used with incoming notification messages. This is used when no color is set for the incoming
             notification message. See README(https://goo.gl/6BKBk7) for more.
        -->
        <meta-data
                android:name="com.google.firebase.messaging.default_notification_color"
                android:resource="@color/colorAccent"/> <!-- [END fcm_default_icon] -->
        <!-- [START fcm_default_channel] -->
        <meta-data
            android:name="com.google.firebase.messaging.default_notification_channel_id"
            android:value="@string/breakfast_notification_channel_id" />
        <!-- [END fcm_default_channel] -->

Хорошей идеей будет создать новый канал уведомлений для FCM, поскольку ваши пользователи могут захотеть включить/отключить таймер для варки яиц или push-уведомления FCM по отдельности.

  1. Откройте ui/EggTimerFragment.kt . В onCreateView() добавьте следующий код создания канала.
// EggTimerFragment.kt

   // TODO: Step 3.1 create a new channel for FCM
    createChannel(
        getString(R.string.breakfast_notification_channel_id),
        getString(R.string.breakfast_notification_channel_name)
    )
  1. Откройте MyFirebaseMessagingService.kt и раскомментируйте функцию onNewToken() . Эта функция будет вызываться при генерации нового токена.
// MyFirebaseMessagingService.kt

   // TODO: Step 3.2 log registration token
    // [START on_new_token]
    /**
     * Called if InstanceID token is updated. This may occur if the security of
     * the previous token had been compromised. Note that this is called when the     
     * InstanceID token is initially generated so this is where you would retrieve     
     * the token.
     */
    override fun onNewToken(token: String?) {
        Log.d(TAG, "Refreshed token: $token")

        // If you want to send messages to this application instance or
        // manage this apps subscriptions on the server side, send the
        // Instance ID token to your app server.
        sendRegistrationToServer(token)
    }
    // [END on_new_token]
  1. Запустите приложение-таймер для варки яиц.
  2. Проверьте logcat ( Вид > Окна инструментов > Logcat ). Вы должны увидеть строку журнала, отображающую ваш токен, похожую на ту, что показана ниже. Это токен, необходимый для отправки сообщения на это устройство. Эта функция вызывается только при создании нового токена.
2019-07-23 13:09:15.243 2312-2459/com.example.android.eggtimernotifications D/MyFirebaseMsgService: Refreshed token: f2esflBoQbI:APA91bFMzNNFaIskjr6KIV4zKjnPA4hxekmrtbrtba2aDbh593WQnm11ed54Mv6MZ9Yeerver7pzgwfKx7R9BHFffLBItLEgPvrtF0TtX9ToCrXZ5y7Hd-m

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

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

  1. Откройте консоль Firebase и выберите свой проект.
  2. Затем выберите Cloud Messaging в навигационной панели слева.
  3. Нажмите «Отправить первое сообщение» .

  1. Введите заголовок уведомления Time for Breakfast! и текст уведомления Don't forget to eat eggs! , затем выберите «Отправить тестовое сообщение» . Появится всплывающее диалоговое окно «Тест на устройстве» с запросом на ввод регистрационного токена FCM.

  1. Скопируйте токен вашего приложения из logcat.

  1. Вставьте этот токен в поле Добавить токен регистрации FCM во всплывающем окне, затем нажмите кнопку Добавить рядом с токеном.
  2. В появившемся списке флажков выберите токен. Кнопка «Тест» должна стать активной.

  1. Переведите приложение Egg Timer на свое устройство в фоновый режим.
  2. Во всплывающем окне нажмите кнопку Тест .
  1. После нажатия кнопки «Тест» целевое клиентское устройство, на котором ваше приложение работает в фоновом режиме, должно получить уведомление в области уведомлений системы. (Более подробную информацию о том, как обрабатывать сообщения FCM, когда ваше приложение работает в фоновом режиме, вы узнаете позже.)

Задача: Отправка уведомлений FCM в тему

Обмен сообщениями по темам FCM основан на модели публикации/подписки.

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

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

Чтобы подписаться на тему, клиентское приложение вызывает функцию Firebase Cloud Messaging subscribeToTopic( ) с названием темы breakfast . Этот вызов может иметь два результата. В случае успеха будет вызван обратный вызов OnCompleteListener с сообщением, на которое была оформлена подписка. Если клиенту не удастся подписаться, обратный вызов получит сообщение об ошибке.

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

  1. Откройте EggTimerFragment.kt и найдите пустую функцию subscribeTopic() .
  2. Получите экземпляр FirebaseMessaging и вызовите функцию subscibeToTopic() с именем темы.
  3. Добавьте addOnCompleteListener , чтобы получать уведомления от FCM об успешной или неудачной подписке.
// EggTimerFragment.kt

   // TODO: Step 3.3 subscribe to breakfast topic
    private fun subscribeTopic() {
        // [START subscribe_topics]
        FirebaseMessaging.getInstance().subscribeToTopic(TOPIC)
            .addOnCompleteListener { task ->
                var msg = getString(R.string.message_subscribed)
                if (!task.isSuccessful) {
                    msg = getString(R.string.message_subscribe_failed)
                }
                Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
            }
        // [END subscribe_topics]
    }
  1. Вызовите функцию subscribeTopic() , чтобы подписаться на тему при запуске приложения. Прокрутите страницу до onCreateView() и добавьте вызов subscribeTopic() .
// EggTimerFragment.kt

   // TODO: Step 3.4 call subscribe topics on start
    subscribeTopic()

    return binding.root
  1. Чтобы подписаться на тему завтрака, запустите приложение ещё раз. Вы увидите всплывающее сообщение «Подписка на тему выполнена».

Теперь вы можете протестировать отправку сообщений в тему:

  1. Откройте редактор уведомлений и выберите «Создать уведомление» .
  2. Установите заголовок уведомления и текст уведомления, как и прежде.
  3. На этот раз вместо отправки сообщения на одно устройство нажмите «Тема» в разделе «Цель» и введите breakfast в качестве темы сообщения.

  1. Для планирования выберите «Сейчас» .

  1. Убедитесь, что ваше приложение работает в фоновом режиме на тестовом устройстве.
  1. Нажмите «Обзор» , а затем « Опубликовать» . Если приложение может работать на нескольких устройствах, вы можете протестировать его и убедиться, что уведомление приходит на все устройства, подписанные на эту тему.

В приложении теперь доступны следующие каналы уведомлений: Egg and Breakfast . На клиентском устройстве нажмите и удерживайте значок приложения, выберите «Информация» и нажмите «Уведомления» . Вы увидите каналы уведомлений Egg and Breakfast , как показано на следующем снимке экрана. Если вы отмените выбор канала Breakfast , ваше приложение не будет получать уведомления по этому каналу.

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

Шаг 1: Сообщения данных

Сообщения FCM также могут содержать полезную нагрузку данных, которая обрабатывает сообщения в клиентском приложении, используйте сообщения данных вместо сообщений-уведомлений.

Для обработки сообщений с данными необходимо обрабатывать полезную нагрузку в функции onMessageReceived() службы MyFirebaseMessagingService . Полезная нагрузка хранится в свойстве data объекта remoteMessage . Как сам объект remoteMessage , так и свойство data могут быть null .

  1. Откройте MyFirebaseMessagingService.
  2. Проверьте, имеет ли свойство data объекта remoteMessage какое-либо значение, и выведите данные в журнал.
// MyFirebaseMessagingService.kt

    // [START receive_message]
    override fun onMessageReceived(remoteMessage: RemoteMessage?) {
        // Not getting messages here? See why this may be: https://goo.gl/39bRNJ
        Log.d(TAG, "From: ${remoteMessage?.from}")
        
       // TODO: Step 3.5 check messages for data
        // Check if the message contains a data payload.
        remoteMessage?.data?.let {
            Log.d(TAG, "Message data payload: " + remoteMessage.data)
        }

    }
    // [END receive_message]

Чтобы протестировать свой код, вы можете снова воспользоваться компоновщиком уведомлений .

  1. Откройте редактор уведомлений, создайте новое сообщение, установив в качестве цели тему «завтрак».
  2. На этот раз, когда вы дойдете до шага 4, Дополнительные параметры , задайте пользовательские свойства ключа и значения данных следующим образом:
  1. Ключ: eggs
  2. Значение: 3

  1. Убедитесь, что ваше приложение работает в фоновом режиме. Если приложение работает в фоновом режиме, сообщение FCM вызовет автоматическое уведомление, а функция onMessageReceived() получит только объект remoteMessage , когда пользователь нажмёт на уведомление.
  2. Отправьте сообщение из компоновщика уведомлений и просмотрите журнал сообщений с данными, который появится в logcat.

Шаг 2: Обработка сообщений на переднем и заднем плане

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

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

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

При отправке сообщения FCM на устройства, на которых установлено приложение-таймер для варки яиц, уведомление отображается автоматически, если приложение не запущено или находится в фоновом режиме. Однако, если приложение работает в активном режиме, уведомление не отображается автоматически; код приложения решает, что делать с сообщением. Если приложение получает сообщение FCM в активном режиме, функция onMessageReceived() будет автоматически вызвана вместе с сообщением FCM. Здесь ваше приложение может автоматически обрабатывать уведомления и данные или инициировать уведомление.

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

  1. Снова откройте функцию onMessageReceived() в MyFirebaseMessagingService .
  2. Сразу после кода, который вы недавно добавили для проверки сообщения с данными, добавьте следующий код, который отправляет уведомление с использованием фреймворка уведомлений.
// MyFirebaseMessagingService.kt

    // TODO: Step 3.6 check messages for notification and call sendNotification
    // Check if the message contains a notification payload.
    remoteMessage.notification?.let {
        Log.d(TAG, "Message Notification Body: ${it.body}")
        sendNotification(it.body as String)
    }
  1. Если вы снова запустите приложение и отправите уведомление с помощью компоновщика уведомлений, вы должны увидеть уведомление, точно такое же, как вы видели в первой части кодовой лаборатории, независимо от того, находится ли приложение на переднем плане или в фоновом режиме.

Код решения находится в основной ветке загруженного вами кода .

  • Реализуйте FCM BroadcastReceiver, расширив FirebaseMessagingService .
  • Настройте проект Firebase Cloud Messaging (FCM) и добавьте FCM в свое приложение Android.
  • Протестируйте свое приложение, отправив push-уведомления из компоновщика уведомлений.
  • Подпишитесь на темы FCM, вызвав функцию subscribeToTopic() класса FirebaseMessaging .
  • Отправьте полезные данные с помощью объекта RemoteMessage .
  • Обработка данных в функции onMessageReceived() .
  • Добавьте логику для обработки FCM, когда приложение находится на переднем плане и когда оно находится в фоновом режиме.

Курс Udacity:

Документация Firebase:

Ссылки на другие практические занятия по этому курсу см. на целевой странице практических занятий по курсу Advanced Android in Kotlin .