Хабр, привет!
Firebase Cloud Messaging (FCM) — это бесплатный (или почти) облачный сервис от Google, который позволяет отправлять push-уведомления и сообщения напрямую на устройства Android, iOS и веб-приложения. FCM широко используется для реализации мгновенных уведомлений, оповещений о событиях, маркетинговых рассылок и других сценариев, где требуется быстрая доставка сообщений пользователям.
В этой статье мы рассмотрим, как интегрировать FCM в Android-приложение на Kotlin и как настроить отправку push-уведомлений с небольшого, похожего backend, сервера, написанного на Kotlin. Вы узнаете, как подключить Firebase к вашему проекту, реализовать обработку входящих сообщений на устройстве, а также научитесь отправлять push-уведомления на выбранные устройства.
Итак, начнём. Заходим на Firebase console и создаем новый проект

На следующем шаге оставляем рекомендуемые параметры для Gemini и аналитики, выбираем аккаунт и завершаем создание проекта. Как только появилась надпись, что проект готов, приступаем к его настройке.
Для начала нужно добавить Android приложение к этому проекту:

На следующем этапе понадобятся данные приложения и подпись.
Я заранее создал пустой проект и добавил туда 3 модуля по чистой архитектуре (presentation, domain, data), чтобы создать видимость реального проекта, т.к. это могут быть как глобальные модули, так и модули внутри фичи. Сделал это не из соображений соблюдения архитектуры, а для того, чтобы показать, куда подключать зависимости и плагины, потому что в одномодульном проекте все понятно и слишком просто.

Теперь у нас есть package name приложения. С никнеймом приложения проблем не возникнет, а вот сертификат подписи сейчас сгенерируем. Следующий параграф можно пропустить тем, у кого уже есть свой keystore.
Создание keystore
Android Studio -> Build -> Generate Signed App Bundle or APK -> здесь APK или Android App Bundle, без разницы -> create new
Далее вводим все необходимые данные, указываем путь к файлу хранилища ключей для сохранения и сохраняем.
Keystore готов, далее можно самостоятельно в студии генерировать подписанный исполняемый файл.
Получение сертификата
Переходим в папку с keystore. Вместе с установкой JDK поставляется утилита keytool для генерации и работы с keystore. Через неё, кстати, можно было и keystore создать.
Команда: keytool.exe --list -v --alias ваш_алиас --keystore хранилище.jks
После нажатия enter нужно ввести пароль хранилища, а потом выведутся SHA-1
и SHA-256
сертификаты.
Возвращаемся в firebase console, вставляем SHA-1, нажимаем "register app", и теперь доступен файл google-services.json
, который нужно поместить в папку модуля app
.

Далее показывается инструкция по добавлению базовых зависимостей.

Плагин: id("com.google.gms.google-services") version "4.4.2" apply false
Зависимости:
//fcm
api(platform("com.google.firebase:firebase-bom:33.14.0"))
api("com.google.firebase:firebase-messaging")
Плагин разместим в app
модуле, а зависимости в data
, т.к. это не относится ни к интерфейсу, ни к бизнес-логике. Теперь заканчиваем с регистрацией приложения.
Создание firebase в android приложении
В классе Application
модуля app
нужно добавить инициализацию firebase:
import android.app.Application
import com.google.firebase.FirebaseApp
internal class App : Application() {
override fun onCreate() {
super.onCreate()
FirebaseApp.initializeApp(this@App)
}
}
За получение данных об уведомлении отвечает FirebaseMessagingService
, который будет находиться в data
модуле. Вместо отправки пуш-уведомления я буду логировать его, иначе статья затянется еще и на добавление сервисов для уведомлений, а также запроса разрешения.
import android.util.Log
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
class MyFirebaseMessagingService : FirebaseMessagingService() {
override fun onMessageReceived(message: RemoteMessage) {
Log.e(
"fcm",
"title=${message.notification?.title}" +
"\ntext=${message.notification?.body}"
)
}
override fun onNewToken(token: String) {
super.onNewToken(token)
Log.e("fcm", "token=$token")
}
}
В манифест app
модуля добавляю декларацию этого сервиса так, чтобы google сервисы могли вызвать его через intent-фильтр:
<service android:name="ru.vafeen.data.MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
Итак, после запуска приложения в logcat
необходимо найти токен устройства и скопировать.
Тестирование
Протестируем работу fcm без бэкенда. На главной странице проекта в firebase console в категории run есть раздел messaging, где можно отправить тестовое сообщение.

Далеепо нажатию на кнопку «create your first compaign» выберем «Firebase Notification messages», которое приходит вне зависимости от нахождения в приложении.
На этом шаге задаем заголовок и текст уведомления.

Далее «Send test message», вставляем туда скопированный токен и нажимам «Test».
Результат:

Работа с Android-частью закончена, переходим к «бэкенду».
Backend
В этом же проекте, для простоты и удобства, я создам обычный Java or Kotlin Library модуль.
Здесь нужно настроить Firebase Admin SDK, чтобы отправлять сообщения.
Зависимость: implementation("com.google.firebase:firebase-admin:9.2.0")
Теперь сгенерируем Service Account key, по которому происходит отправка сообщений. Firebase Console -> Project settings -> Service Accounts -> generate new private key -> generate key -> сохраняем в удобное место
Также здесь сразу даётся сниппет для инициализации SDK: копируем его.
Далее инициализируем SDK, получаем инстанс FCM, создаем сообщение и отправляем его. Токен — харкдод, т. к. это не полноценный бэкенд, и его отправка выходит за рамки статьи.
import com.google.auth.oauth2.GoogleCredentials
import com.google.firebase.FirebaseApp
import com.google.firebase.FirebaseOptions
import com.google.firebase.messaging.FirebaseMessaging
import com.google.firebase.messaging.Message
import com.google.firebase.messaging.Notification
import java.io.FileInputStream
const val token = "токен девайса"
fun main() {
initialFirebase()
val fcm = FirebaseMessaging.getInstance()
val message = Message.builder().setNotification(
Notification.builder().setTitle("test sdk title").setBody("test sdk text").build()
).setToken(token).build()
println(fcm.send(message))
}
fun initialFirebase() {
val serviceAccount = FileInputStream("path/to/serviceAccountKey.json")
val options = FirebaseOptions.builder()
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
.build()
FirebaseApp.initializeApp(options)
}
А также при запуске бэкенд потребует логирование. Здесь просто происходит чек наличия, поэтому на настройке не будем останавливаться.
Добавляем зависимость: implementation("ch.qos.logback:logback-classic:1.5.18")
И снова запуск.

На этом путь от пустого проекта до отправки fcm с имитированного бэкенда пройден!
No errors, no warnings, gentlemen and ladies!