| Wybierz platformę: | iOS+ Android Web Flutter Unity C++ |
Wiadomości przychodzące są obsługiwane inaczej w zależności od stanu urządzenia. Aby zrozumieć te scenariusze i dowiedzieć się, jak zintegrować FCM z własną aplikacją, najpierw musisz poznać różne stany, w jakich może znajdować się urządzenie:
| Stan | Opis |
|---|---|
| Pierwszy plan | gdy aplikacja jest otwarta, widoczna i używana; |
| Informacje ogólne | gdy aplikacja jest otwarta, ale działa w tle (jest zminimalizowana); Zwykle dzieje się tak, gdy użytkownik naciśnie przycisk „Ekran główny” na urządzeniu, przełączy się na inną aplikację za pomocą przełącznika aplikacji lub otworzy aplikację na innej karcie (w internecie). |
| Zakończona | gdy urządzenie jest zablokowane lub aplikacja nie jest uruchomiona; |
Zanim aplikacja będzie mogła odbierać ładunki wiadomości za pomocą FCM, musi spełnić kilka warunków wstępnych:
- Aplikacja musi zostać otwarta co najmniej raz (aby umożliwić rejestrację w FCM).
- Jeśli użytkownik usunie aplikację z przełącznika aplikacji na iOS, musi ją ponownie otworzyć ręcznie, aby wiadomości w tle znów zaczęły działać.
- Jeśli na urządzeniu z Androidem użytkownik wymusi zamknięcie aplikacji w ustawieniach urządzenia, musi ją ponownie otworzyć ręcznie, aby wiadomości zaczęły działać.
- W przypadku internetu musisz poprosić o token (za pomocą
getToken()) z certyfikatem powiadomień push w internecie.
Prośba o uprawnienia do otrzymywania wiadomości
W systemach iOS, macOS, Android 13 (lub nowszym) oraz w internecie, zanim ładunki FCM będą mogły być odbierane na urządzeniu, musisz najpierw poprosić użytkownika o zgodę.
Pakiet firebase_messaging udostępnia interfejs API do wysyłania próśb o uprawnienia za pomocą metody requestPermission. Ten interfejs API akceptuje szereg nazwanych argumentów, które definiują typ uprawnień, o które chcesz poprosić, np. czy wiadomości zawierające ładunki powiadomień mogą wywoływać dźwięk lub odczytywać wiadomości za pomocą Siri. Domyślnie metoda żąda odpowiednich uprawnień domyślnych. W dokumentacji interfejsu API znajdziesz pełne informacje o tym, do czego służy każde uprawnienie.
Aby rozpocząć, wywołaj metodę z aplikacji (na iOS wyświetli się wbudowane okno modalne, a w internecie zostanie uruchomiony przepływ interfejsu API przeglądarki):
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}');
Właściwość authorizationStatus obiektu NotificationSettings zwrócona w odpowiedzi na żądanie może służyć do określenia ogólnej decyzji użytkownika:
authorized: użytkownik przyznał uprawnienia.denied: użytkownik odmówił uprawnień.notDetermined: użytkownik nie podjął jeszcze decyzji w sprawie udzielenia zgody,provisional: użytkownik przyznał tymczasowe uprawnienia.
Inne właściwości w NotificationSettings zwracają informację o tym, czy określone uprawnienie jest włączone, wyłączone czy nieobsługiwane na bieżącym urządzeniu.
Po uzyskaniu uprawnień i zrozumieniu różnych typów stanu urządzenia aplikacja może zacząć obsługiwać przychodzące ładunki FCM.
Obsługa wiadomości
W zależności od bieżącego stanu aplikacji przychodzące ładunki różnych typów wiadomości wymagają różnych implementacji do ich obsługi:
Wiadomości na pierwszym planie
Aby obsługiwać wiadomości, gdy aplikacja działa na pierwszym planie, nasłuchuj strumienia 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}');
}
});
Strumień zawiera obiekt RemoteMessage, który zawiera różne informacje o ładunku, takie jak miejsce pochodzenia, unikalny identyfikator, czas wysłania, informacja o tym, czy zawierał powiadomienie, i inne. Wiadomość została pobrana, gdy aplikacja działała na pierwszym planie, więc możesz bezpośrednio uzyskać dostęp do stanu i kontekstu aplikacji Flutter.
Wiadomości na pierwszym planie i wiadomości z powiadomieniem
Wiadomości z powiadomieniami, które docierają, gdy aplikacja działa na pierwszym planie, domyślnie nie wyświetlają widocznego powiadomienia zarówno na Androidzie, jak i na iOS. Można jednak zastąpić to działanie:
- Na Androidzie musisz utworzyć kanał powiadomień o wysokim priorytecie.
- W systemie iOS możesz zaktualizować opcje prezentacji aplikacji.
Wiadomości w tle
Proces obsługi wiadomości w tle różni się na platformach Android, Apple i internetowych.
Platformy Apple i Android
Obsługuj wiadomości w tle, rejestrując moduł obsługi onBackgroundMessage. Po odebraniu wiadomości tworzony jest odizolowany proces (tylko na Androidzie, iOS/macOS nie wymaga osobnego odizolowanego procesu), co umożliwia obsługę wiadomości nawet wtedy, gdy aplikacja nie jest uruchomiona.
Pamiętaj o kilku kwestiach dotyczących modułu obsługi wiadomości w tle:
- Nie może to być funkcja anonimowa.
- Musi to być funkcja najwyższego poziomu (np. nie metoda klasy, która wymaga inicjalizacji).
- Jeśli używasz Fluttera w wersji 3.3.0 lub nowszej, moduł obsługi wiadomości musi być oznaczony adnotacją
@pragma('vm:entry-point')bezpośrednio nad deklaracją funkcji (w przeciwnym razie może zostać usunięty podczas eliminacji martwego kodu w trybie wersji).
@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());
}
Ponieważ moduł obsługi działa we własnym odizolowanym środowisku poza kontekstem aplikacji, nie można aktualizować stanu aplikacji ani wykonywać żadnych działań wpływających na interfejs. Możesz jednak wykonywać operacje logiczne, takie jak żądania HTTP, operacje wejścia/wyjścia (np. aktualizowanie pamięci lokalnej), komunikacja z innymi wtyczkami itp.
Zalecamy też jak najszybsze ukończenie logiki. Wykonywanie długich, intensywnych zadań wpływa na wydajność urządzenia i może spowodować, że system operacyjny zakończy proces. Jeśli zadania trwają dłużej niż 30 sekund, urządzenie może automatycznie zakończyć proces.
Sieć
W internecie napisz w języku JavaScript skrypt service worker, który działa w tle. Użyj skryptu service worker do obsługi wiadomości w tle.
Na początek utwórz nowy plik w katalogu web i nazwij go firebase-messaging-sw.js:
// See this file for the latest firebase-js-sdk version:
// https://github.com/firebase/flutterfire/blob/main/packages/firebase_core/firebase_core_web/lib/src/firebase_sdk_version.dart
importScripts("https://www.gstatic.com/firebasejs/10.7.0/firebase-app-compat.js");
importScripts("https://www.gstatic.com/firebasejs/10.7.0/firebase-messaging-compat.js");
firebase.initializeApp({
apiKey: "...",
authDomain: "...",
databaseURL: "...",
projectId: "...",
storageBucket: "...",
messagingSenderId: "...",
appId: "...",
});
const messaging = firebase.messaging();
// Optional:
messaging.onBackgroundMessage((message) => {
console.log("onBackgroundMessage", message);
});
Plik musi importować pakiety SDK aplikacji i wiadomości, inicjować Firebase i udostępniać zmienną messaging.
Następnie musisz zarejestrować pracownika. W pliku index.html zarejestruj skrypt service worker, modyfikując tag <script>, który uruchamia Fluttera:
<script src="flutter_bootstrap.js" async>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('firebase-messaging-sw.js', {
scope: '/firebase-cloud-messaging-push-scope',
});
});
}
</script>
Jeśli nadal używasz starego systemu szablonów, możesz zarejestrować skrypt, modyfikując tag <script>, który uruchamia Fluttera, w ten sposób:
<html>
<body>
<script>
var serviceWorkerVersion = null;
var scriptLoaded = false;
function loadMainDartJs() {
if (scriptLoaded) {
return;
}
scriptLoaded = true;
var scriptTag = document.createElement('script');
scriptTag.src = 'main.dart.js';
scriptTag.type = 'application/javascript';
document.body.append(scriptTag);
}
if ('serviceWorker' in navigator) {
// Service workers are supported. Use them.
window.addEventListener('load', function () {
// Register Firebase Messaging service worker.
navigator.serviceWorker.register('firebase-messaging-sw.js', {
scope: '/firebase-cloud-messaging-push-scope',
});
// 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;
navigator.serviceWorker.register(serviceWorkerUrl).then((reg) => {
function waitForActivation(serviceWorker) {
serviceWorker.addEventListener('statechange', () => {
if (serviceWorker.state == 'activated') {
console.log('Installed new service worker.');
loadMainDartJs();
}
});
}
if (!reg.active && (reg.installing || reg.waiting)) {
// No active web worker and we have installed or are installing
// one for the first time. Simply wait for it to activate.
waitForActivation(reg.installing ?? reg.waiting);
} else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
// When the app updates the serviceWorkerVersion changes, so we
// need to ask the service worker to update.
console.log('New service worker available.');
reg.update();
waitForActivation(reg.installing);
} else {
// Existing service worker is still good.
console.log('Loading app from service worker.');
loadMainDartJs();
}
});
// If service worker doesn't succeed in a reasonable amount of time,
// fallback to plaint <script> tag.
setTimeout(() => {
if (!scriptLoaded) {
console.warn(
'Failed to load app from service worker. Falling back to plain <script> tag.'
);
loadMainDartJs();
}
}, 4000);
});
} else {
// Service workers not supported. Just drop the <script> tag.
loadMainDartJs();
}
</script>
</body>
Następnie uruchom ponownie aplikację Flutter. Skrypt zostanie zarejestrowany, a wszystkie wiadomości w tle będą obsługiwane za pomocą tego pliku.
Obsługa interakcji
Powiadomienia są widoczne, więc użytkownicy często wchodzą z nimi w interakcję (naciskając je). Domyślnym działaniem zarówno na Androidzie, jak i na iOS jest otwarcie aplikacji. Jeśli aplikacja została zamknięta, zostanie uruchomiona. Jeśli działa w tle, zostanie przeniesiona na pierwszy plan.
W zależności od treści powiadomienia możesz chcieć obsłużyć interakcję użytkownika po otwarciu aplikacji. Jeśli na przykład nowa wiadomość na czacie zostanie wysłana za pomocą powiadomienia, a użytkownik je naciśnie, możesz otworzyć konkretną rozmowę po otwarciu aplikacji.
Pakiet firebase-messaging udostępnia 2 sposoby obsługi tej interakcji:
getInitialMessage(): jeśli aplikacja zostanie otwarta ze stanu zamknięcia, zostanie zwrócony obiektFuturezawierającyRemoteMessage. Po wykorzystaniuRemoteMessagezostanie usunięty.onMessageOpenedApp:Stream, który publikujeRemoteMessage, gdy aplikacja jest otwierana ze stanu działania w tle.
Zalecamy obsługę obu scenariuszy, aby zapewnić użytkownikom płynne działanie. Poniższy przykładowy kod pokazuje, jak to zrobić:
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 using 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("...");
}
}
Sposób obsługi interakcji zależy od konfiguracji aplikacji. Poprzedni przykład pokazuje podstawową ilustrację z użyciem StatefulWidget.
Lokalizowanie wiadomości
Zlokalizowane ciągi znaków możesz wysyłać na 2 sposoby:
- Zapisuj preferowany język każdego użytkownika na serwerze i wysyłaj spersonalizowane powiadomienia w każdym języku.
- Umieść w aplikacji zlokalizowane ciągi tekstowe i wykorzystaj wbudowane ustawienia regionalne systemu operacyjnego.
Aby użyć drugiej metody:
Android
Określ wiadomości w języku domyślnym w pliku
resources/values/strings.xml:<string name="notification_title">Hello world</string> <string name="notification_message">This is a message</string>Określ przetłumaczone wiadomości w katalogu
values-language. Na przykład określ komunikaty w języku francuskim w plikuresources/values-fr/strings.xml:<string name="notification_title">Bonjour le monde</string> <string name="notification_message">C'est un message</string>W ładunku serwera zamiast kluczy
title,messageibodyużyj kluczytitle_loc_keyibody_loc_keydla zlokalizowanej wiadomości i ustaw je na atrybutnamewiadomości, którą chcesz wyświetlić.Ładunek wiadomości będzie wyglądał tak:
{ "android": { "notification": { "title_loc_key": "notification_title", "body_loc_key": "notification_message" } } }
iOS
Określ wiadomości w języku domyślnym w pliku
Base.lproj/Localizable.strings:"NOTIFICATION_TITLE" = "Hello World"; "NOTIFICATION_MESSAGE" = "This is a message";Określ przetłumaczone wiadomości w katalogu
language.lproj. Na przykład określ komunikaty w języku francuskim w plikufr.lproj/Localizable.strings:"NOTIFICATION_TITLE" = "Bonjour le monde"; "NOTIFICATION_MESSAGE" = "C'est un message";Ładunek wiadomości będzie wyglądał tak:
{ "apns": { "payload": { "alert": { "title-loc-key": "NOTIFICATION_TITLE", "loc-key": "NOTIFICATION_MESSAGE" } } } }
Włączanie eksportu danych o dostarczaniu wiadomości
Możesz wyeksportować dane wiadomości do BigQuery, aby przeprowadzić ich dalszą analizę. BigQuery umożliwia analizowanie danych za pomocą BigQuery SQL, eksportowanie ich do innego dostawcy chmury lub używanie ich w niestandardowych modelach ML. Eksport do BigQuery obejmuje wszystkie dostępne dane dotyczące wiadomości, niezależnie od ich typu i tego, czy zostały wysłane za pomocą interfejsu API czy kreatora powiadomień.
Aby włączyć eksportowanie, najpierw wykonaj czynności opisane w dokumencie Eksportowanie danych do BigQuery. Włączenie tej funkcji automatycznie na poziomie instancji aplikacji pozwala poprosić użytkowników o zgodę na analizowanie danych o dostarczaniu wiadomości (zalecane). Aby automatycznie włączyć eksport, wykonaj te instrukcje:
Android
Możesz użyć tego kodu:
await FirebaseMessaging.instance.setDeliveryMetricsExportToBigQuery(true);
iOS
W przypadku iOS musisz zmienić AppDelegate.m na następującą treść.
#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
Sieć
W przypadku aplikacji internetowych musisz zmienić skrypt service worker, aby używać wersji 9 pakietu SDK. Wersja 9 musi być spakowana, więc aby skrypt service worker działał, musisz użyć pakietu takiego jak esbuild. Aby dowiedzieć się, jak to zrobić, zobacz przykładową aplikację.
Po przejściu na pakiet SDK w wersji 9 możesz użyć tego kodu:
import {
experimentalSetDeliveryMetricsExportedToBigQueryEnabled,
getMessaging,
} from 'firebase/messaging/sw';
...
const messaging = getMessaging(app);
experimentalSetDeliveryMetricsExportedToBigQueryEnabled(messaging, true);
Pamiętaj, aby uruchomić yarn build, aby wyeksportować nową wersję skryptu service worker do folderu web.
Wyświetlanie obrazów w powiadomieniach na iOS
Aby na urządzeniach Apple przychodzące powiadomienia FCM wyświetlały obrazy z ładunku FCM, musisz dodać rozszerzenie usługi powiadomień i skonfigurować aplikację tak, aby go używała.
Jeśli używasz uwierzytelniania Firebase przez telefon, musisz dodać do pliku Podfile pod Firebase Auth.
Krok 1. Dodaj rozszerzenie usługi powiadomień
- W Xcode kliknij File > New > Target… (Plik > Nowy > Cel…).
- Pojawi się okno z listą możliwych elementów docelowych. Przewiń do opcji Notification Service Extension (Rozszerzenie usługi powiadomień) lub użyj filtra, aby ją wybrać. Kliknij Dalej.
- Dodaj nazwę produktu (użyj „ImageNotification”, aby postępować zgodnie z tym samouczkiem), wybierz
SwiftlubObjective-Ci kliknij Zakończ. - Włącz schemat, klikając Aktywuj.
Krok 2. Dodaj cel do pliku Podfile
Swift
Upewnij się, że nowe rozszerzenie ma dostęp do pakietu Swift FirebaseMessaging, dodając go do celu Runner:
W Nawigatorze dodaj pakiet SDK Firebase na platformy Apple: Plik > Dodaj zależności pakietu….
Wyszukaj lub wpisz URL pakietu:
none https://github.com/firebase/firebase-ios-sdkDodaj do projektu
Runner: Dodaj pakietWybierz FirebaseMessaging i dodaj do docelowego ImageNotification: Add Package (Dodaj pakiet).
Objective-C
Upewnij się, że nowe rozszerzenie ma dostęp do poda Firebase/Messaging, dodając go w pliku Podfile:
W Nawigatorze otwórz plik Podfile: Pods > Podfile.
Przejdź na dół pliku i dodaj:
target 'ImageNotification' do use_frameworks! pod 'Firebase/Auth' # Add this line if you are using FirebaseAuth phone authentication pod 'Firebase/Messaging' endZainstaluj lub zaktualizuj pody za pomocą polecenia
pod installz kataloguioslubmacos.
Krok 3. Użyj asystenta rozszerzeń
Wszystko powinno działać normalnie. Ostatnim krokiem jest wywołanie pomocnika rozszerzenia.
Swift
W nawigatorze wybierz rozszerzenie ImageNotification.
Otwórz plik
NotificationService.swift.Zastąp zawartość pliku
NotificationService.swifttym kodem:import UserNotifications import FirebaseMessaging class NotificationService: UNNotificationServiceExtension { var contentHandler: ((UNNotificationContent) -> Void)? var bestAttemptContent: UNMutableNotificationContent? override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) Messaging.serviceExtension().populateNotificationContent(bestAttemptContent!, withContentHandler: contentHandler) } override func serviceExtensionTimeWillExpire() { if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { contentHandler(bestAttemptContent) } } }
Objective-C
W nawigatorze wybierz rozszerzenie ImageNotification.
Otwórz plik
NotificationService.m.Na początku pliku zaimportuj
FirebaseMessaging.hzaraz poNotificationService.h.Zastąp zawartość pliku
NotificationService.mtym kodem:#import "NotificationService.h" #import "FirebaseMessaging.h" #import <FirebaseAuth/FirebaseAuth-Swift.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 () <NSURLSessionDelegate> @property(nonatomic) void (^contentHandler)(UNNotificationContent *contentToDeliver); @property(nonatomic) 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
Krok 4. Dodaj obraz do ładunku
Do ładunku powiadomienia możesz teraz dodać obraz. Więcej informacji znajdziesz w artykule na temat tworzenia żądania wysłania.