В зависимости от состояния устройства входящие сообщения обрабатываются по-разному. Чтобы понять эти сценарии и то, как интегрировать FCM в ваше собственное приложение, сначала важно установить различные состояния, в которых может находиться устройство:
Состояние | Описание |
---|---|
Передний план | Когда приложение открыто, просматривается и используется. |
Фон | Когда приложение открыто, но в фоновом режиме (свернуто). Обычно это происходит, когда пользователь нажал кнопку «Домой» на устройстве, переключился на другое приложение с помощью переключателя приложений или открыл приложение на другой вкладке (в Интернете). |
Прекращено | Когда устройство заблокировано или приложение не запущено. |
Есть несколько предварительных условий, которые должны быть выполнены, прежде чем приложение сможет получать полезные данные сообщения через FCM:
- Приложение должно быть открыто хотя бы один раз (для регистрации в FCM).
- В iOS, если пользователь удаляет приложение из переключателя приложений, его необходимо снова открыть вручную, чтобы фоновые сообщения снова начали работать.
- На Android, если пользователь принудительно закрывает приложение из настроек устройства, его необходимо снова открыть вручную, чтобы сообщения начали работать.
- В Интернете вы должны запросить токен (используя
getToken()
) с вашим сертификатом веб-push.
Запросить разрешение на получение сообщений (Apple и Web)
В iOS, macOS и Интернете, прежде чем на ваше устройство можно будет получать полезные данные FCM, вы должны сначала запросить разрешение пользователя.
Пакет firebase_messaging
предоставляет простой API для запроса разрешения с помощью метода requestPermission
. Этот API принимает ряд именованных аргументов, которые определяют тип разрешений, которые вы хотели бы запросить, например, могут ли сообщения, содержащие полезные данные уведомлений, запускать звук или зачитывать сообщения через Siri. По умолчанию метод запрашивает разумные разрешения по умолчанию. Эталонный API предоставляет полную документацию о том, для чего предназначено каждое разрешение.
Для начала вызовите метод из своего приложения (в iOS будет отображаться собственный модальный режим, в Интернете будет запущен собственный поток API браузера):
FirebaseMessaging messaging = FirebaseMessaging.instance;
NotificationSettings settings = await messaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
print('User granted permission: ${settings.authorizationStatus}');
Свойство authorizationStatus
объекта NotificationSettings
, возвращенное из запроса, можно использовать для определения общего решения пользователя:
-
authorized
: Пользователь предоставил разрешение. -
denied
: Пользователь отклонил разрешение. -
notDetermined
: пользователь еще не решил, следует ли предоставлять разрешение. -
provisional
: пользователь предоставил временное разрешение
Другие свойства NotificationSettings
возвращают информацию о том, включено ли конкретное разрешение, отключено или не поддерживается на текущем устройстве.
После предоставления разрешения и понимания различных типов состояния устройства ваше приложение может начать обрабатывать входящие полезные данные FCM.
Обработка сообщений
В зависимости от текущего состояния вашего приложения входящие полезные данные различных типов сообщений требуют разных реализаций для их обработки:
Сообщения переднего плана
Чтобы обрабатывать сообщения, пока ваше приложение находится на переднем плане, прослушивайте поток onMessage
.
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Got a message whilst in the foreground!');
print('Message data: ${message.data}');
if (message.notification != null) {
print('Message also contained a notification: ${message.notification}');
}
});
Поток содержит RemoteMessage
, детализирующую различную информацию о полезной нагрузке, например, откуда она была получена, уникальный идентификатор, время отправки, наличие уведомления и многое другое. Поскольку сообщение было получено, когда ваше приложение находится на переднем плане, вы можете напрямую получить доступ к состоянию и контексту вашего приложения Flutter.
Сообщения переднего плана и уведомления
Сообщения с уведомлениями, которые приходят, когда приложение находится на переднем плане, по умолчанию не отображают видимое уведомление как на Android, так и на iOS. Однако это поведение можно переопределить:
- На Android необходимо создать канал уведомлений «Высокий приоритет».
- В iOS вы можете обновить параметры презентации для приложения.
Фоновые сообщения
Процесс обработки фоновых сообщений отличается на собственных (Android и Apple) и веб-платформах.
Платформы Apple и Android
Обрабатывайте фоновые сообщения, регистрируя обработчик onBackgroundMessage
. При получении сообщений создается изолят (только для Android, iOS/macOS не требует отдельного изолята), позволяющий обрабатывать сообщения, даже если ваше приложение не запущено.
Есть несколько вещей, о которых следует помнить в отношении обработчика фоновых сообщений:
- Это не должна быть анонимная функция.
- Это должна быть функция верхнего уровня (например, не метод класса, требующий инициализации).
- При использовании Flutter версии 3.3.0 или выше обработчик сообщений должен быть аннотирован с помощью
@pragma('vm:entry-point')
прямо над объявлением функции (в противном случае он может быть удален во время встряхивания дерева для режима выпуска).
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// If you're going to use other Firebase services in the background, such as Firestore,
// make sure you call `initializeApp` before using other Firebase services.
await Firebase.initializeApp();
print("Handling a background message: ${message.messageId}");
}
void main() {
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
Поскольку обработчик работает в своем собственном изолировании вне контекста вашего приложения, невозможно обновить состояние приложения или выполнить какую-либо логику пользовательского интерфейса, влияющую на него. Однако вы можете выполнять логику, такую как HTTP-запросы, выполнять операции ввода-вывода (например, обновление локального хранилища), взаимодействовать с другими плагинами и т. д.
Также рекомендуется завершить свою логику как можно скорее. Выполнение длительных и интенсивных задач влияет на производительность устройства и может привести к завершению процесса операционной системой. Если задачи выполняются дольше 30 секунд, устройство может автоматически завершить процесс.
Интернет
В Интернете напишите Service Worker JavaScript, который работает в фоновом режиме. Используйте сервис-воркер для обработки фоновых сообщений.
Для начала создайте новый файл в своем web
каталоге и назовите его firebase-messaging-sw.js
:
importScripts("https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js");
importScripts("https://www.gstatic.com/firebasejs/8.10.0/firebase-messaging.js");
firebase.initializeApp({
apiKey: "...",
authDomain: "...",
databaseURL: "...",
projectId: "...",
storageBucket: "...",
messagingSenderId: "...",
appId: "...",
});
const messaging = firebase.messaging();
// Optional:
messaging.onBackgroundMessage((message) => {
console.log("onBackgroundMessage", message);
});
Файл должен импортировать SDK приложения и обмена сообщениями, инициализировать Firebase и предоставить переменную обмена messaging
.
Далее рабочий должен быть зарегистрирован. В файле ввода после загрузки файла main.dart.js
зарегистрируйте своего воркера:
<html>
<body>
...
<script src="main.dart.js" type="application/javascript"></script>
<script>
if ('serviceWorker' in navigator) {
// Service workers are supported. Use them.
window.addEventListener('load', function () {
// ADD THIS LINE
navigator.serviceWorker.register('/firebase-messaging-sw.js');
// Wait for registration to finish before dropping the <script> tag.
// Otherwise, the browser will load the script multiple times,
// potentially different versions.
var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
// ...
});
}
</script>
Затем перезапустите приложение Flutter. Рабочий процесс будет зарегистрирован, и любые фоновые сообщения будут обрабатываться через этот файл.
Обработка взаимодействия
Поскольку уведомления являются видимой подсказкой, пользователи обычно взаимодействуют с ними (нажимая). Поведение по умолчанию как на Android, так и на iOS — открыть приложение. Если приложение будет завершено, оно будет запущено; если он находится на заднем плане, он будет перемещен на передний план.
В зависимости от содержимого уведомления может потребоваться обработка действий пользователя при открытии приложения. Например, если новое сообщение чата отправляется через уведомление, и пользователь нажимает его, вы можете открыть определенный разговор при открытии приложения.
Пакет firebase-messaging
предоставляет два способа обработки этого взаимодействия:
-
getInitialMessage()
: если приложение открыто из завершенного состояния, будет возвращеноFuture
, содержащееRemoteMessage
. После использованияRemoteMessage
будет удален. -
onMessageOpenedApp
:Stream
, который отправляетRemoteMessage
, когда приложение открывается из фонового состояния.
Рекомендуется обрабатывать оба сценария, чтобы обеспечить плавный UX для ваших пользователей. В приведенном ниже примере кода показано, как этого можно добиться:
class Application extends StatefulWidget {
@override
State<StatefulWidget> createState() => _Application();
}
class _Application extends State<Application> {
// It is assumed that all messages contain a data field with the key 'type'
Future<void> setupInteractedMessage() async {
// Get any messages which caused the application to open from
// a terminated state.
RemoteMessage? initialMessage =
await FirebaseMessaging.instance.getInitialMessage();
// If the message also contains a data property with a "type" of "chat",
// navigate to a chat screen
if (initialMessage != null) {
_handleMessage(initialMessage);
}
// Also handle any interaction when the app is in the background via a
// Stream listener
FirebaseMessaging.onMessageOpenedApp.listen(_handleMessage);
}
void _handleMessage(RemoteMessage message) {
if (message.data['type'] == 'chat') {
Navigator.pushNamed(context, '/chat',
arguments: ChatArguments(message),
);
}
}
@override
void initState() {
super.initState();
// Run code required to handle interacted messages in an async function
// as initState() must not be async
setupInteractedMessage();
}
@override
Widget build(BuildContext context) {
return Text("...");
}
}
То, как вы обрабатываете взаимодействие, зависит от настройки вашего приложения. В приведенном выше примере показана базовая иллюстрация с использованием StatefulWidget.
Локализация сообщений
Вы можете отправлять локализованные строки двумя способами:
- Сохраняйте предпочтительный язык каждого из ваших пользователей на своем сервере и отправляйте настраиваемые уведомления для каждого языка.
- Встраивайте локализованные строки в свое приложение и используйте собственные языковые настройки операционной системы.
Вот как использовать второй метод:
Андроид
Укажите сообщения на языке по умолчанию в
resources/values/strings.xml
:<string name="notification_title">Hello world</string> <string name="notification_message">This is a message</string>
Укажите переведенные сообщения в каталоге
values- language
. Например, укажите сообщения на французском языке вresources/values-fr/strings.xml
:<string name="notification_title">Bonjour le monde</string> <string name="notification_message">C'est un message</string>
В полезной нагрузке сервера вместо использования ключей
title
,message
иbody
используйтеtitle_loc_key
иbody_loc_key
для локализованного сообщения и установите для них атрибутname
сообщения, которое вы хотите отобразить.Полезная нагрузка сообщения будет выглядеть так:
{ "data": { "title_loc_key": "notification_title", "body_loc_key": "notification_message" } }
iOS
Укажите язык сообщений по умолчанию в
Base.lproj/Localizable.strings
:"NOTIFICATION_TITLE" = "Hello World"; "NOTIFICATION_MESSAGE" = "This is a message";
Укажите переведенные сообщения в
language .lproj
. Например, укажите сообщения на французском языке вfr.lproj/Localizable.strings
:"NOTIFICATION_TITLE" = "Bonjour le monde"; "NOTIFICATION_MESSAGE" = "C'est un message";
Полезная нагрузка сообщения будет выглядеть так:
{ "data": { "title_loc_key": "NOTIFICATION_TITLE", "body_loc_key": "NOTIFICATION_MESSAGE" } }
Включить экспорт данных о доставке сообщений
Вы можете экспортировать данные сообщения в BigQuery для дальнейшего анализа. BigQuery позволяет анализировать данные с помощью BigQuery SQL, экспортировать их в другой облачный провайдер или использовать данные для своих пользовательских моделей машинного обучения. Экспорт в BigQuery включает все доступные данные для сообщений, независимо от типа сообщения и от того, отправлено ли сообщение через API или компоновщик уведомлений.
Чтобы включить экспорт, сначала выполните шаги, описанные здесь , а затем следуйте этим инструкциям:
Андроид
Вы можете использовать следующий код:
await FirebaseMessaging.instance.setDeliveryMetricsExportToBigQuery(true);
iOS
Для iOS вам нужно изменить AppDelegate.m
со следующим содержимым.
#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"
#import <Firebase/Firebase.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[[FIRMessaging extensionHelper] exportDeliveryMetricsToBigQueryWithMessageInfo:userInfo];
}
@end
Интернет
Для Интернета вам необходимо изменить своего сервисного работника, чтобы использовать версию SDK v9. Версия v9 должна быть связана, поэтому вам нужно использовать сборщик, например esbuild
, чтобы заставить работника службы работать. См. пример приложения , чтобы узнать, как этого добиться.
После перехода на SDK v9 вы можете использовать следующий код:
import {
experimentalSetDeliveryMetricsExportedToBigQueryEnabled,
getMessaging,
} from 'firebase/messaging/sw';
...
const messaging = getMessaging(app);
experimentalSetDeliveryMetricsExportedToBigQueryEnabled(messaging, true);
Не забудьте запустить yarn build
, чтобы экспортировать новую версию сервис-воркера в web
папку.
Отображение изображений в уведомлениях на iOS
На устройствах Apple, чтобы входящие уведомления FCM отображали изображения из полезной нагрузки FCM, необходимо добавить дополнительное расширение службы уведомлений и настроить приложение для его использования.
Если вы используете аутентификацию телефона Firebase, вы должны добавить модуль аутентификации Firebase в свой подфайл.
Шаг 1. Добавьте расширение службы уведомлений
- В Xcode нажмите «Файл» > «Создать» > «Цель...».
- Модальное окно представит список возможных целей; прокрутите вниз или воспользуйтесь фильтром, чтобы выбрать Расширение службы уведомлений . Нажмите «Далее» .
- Добавьте название продукта (используйте «ImageNotification», чтобы следовать этому руководству), установите язык на Objective-C и нажмите «Готово» .
- Включите схему, нажав Активировать .
Шаг 2 — Добавьте цель в подфайл
Убедитесь, что у вашего нового расширения есть доступ к модулю Firebase/Messaging
, добавив его в Podfile:
В навигаторе откройте подфайл: Pods > Podfile.
Прокрутите вниз до конца файла и добавьте:
target 'ImageNotification' do use_frameworks! pod 'Firebase/Auth' # Add this line if you are using FirebaseAuth phone authentication pod 'Firebase/Messaging' end
Установите или обновите свои модули с помощью
pod install
из каталогаios
илиmacos
.
Шаг 3. Используйте помощник расширения
На данный момент все должно работать нормально. Последним шагом является вызов помощника расширения.
В навигаторе выберите расширение ImageNotification.
Откройте файл
NotificationService.m
.В верхней части файла импортируйте
FirebaseMessaging.h
сразу послеNotificationService.h
, как показано ниже.Замените содержимое
NotificationService.m
на:#import "NotificationService.h" #import "FirebaseMessaging.h" #import "FirebaseAuth.h" // Add this line if you are using FirebaseAuth phone authentication #import <UIKit/UIKit.h> // Add this line if you are using FirebaseAuth phone authentication @interface NotificationService () @property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver); @property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent; @end @implementation NotificationService /* Uncomment this if you are using Firebase Auth - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options { if ([[FIRAuth auth] canHandleURL:url]) { return YES; } return NO; } - (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts { for (UIOpenURLContext *urlContext in URLContexts) { [FIRAuth.auth canHandleURL:urlContext.URL]; } } */ - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; // Modify the notification content here... [[FIRMessaging extensionHelper] populateNotificationContent:self.bestAttemptContent withContentHandler:contentHandler]; } - (void)serviceExtensionTimeWillExpire { // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. self.contentHandler(self.bestAttemptContent); } @end
Шаг 4. Добавьте изображение в полезную нагрузку.
В полезной нагрузке уведомлений теперь вы можете добавить изображение. См. документацию iOS о том , как создать запрос на отправку . Имейте в виду, что максимальный размер изображения 300 КБ определяется устройством.