| Wybierz platformę: | iOS+ Android Sieć Flutter Unity C++ |
W zależności od stanu urządzenia wiadomości przychodzące są obsługiwane inaczej. Aby zrozumieć te scenariusze i dowiedzieć się, jak zintegrować FCM z własną aplikacją, musisz najpierw poznać różne stany, w jakich może znajdować się urządzenie:
| Stan | Opis |
|---|---|
| Pierwszy plan | Aplikacja jest otwarta, widoczna i używana. |
| Tło | Aplikacja jest otwarta, ale działa w tle (jest zminimalizowana). Zwykle dzieje się tak, gdy użytkownik naciśnie przycisk ekranu głównego na urządzeniu, przełączy się na inną aplikację za pomocą przełącznika aplikacji lub otworzy aplikację w innej karcie (w sieci). |
| Zakończona | Urządzenie jest zablokowane lub aplikacja nie jest uruchomiona. |
Aby aplikacja mogła odbierać ładunki wiadomości za pomocą FCM, musi spełniać kilka warunków wstępnych:
- Aplikacja musi zostać otwarta co najmniej raz (aby umożliwić rejestrację w FCM).
- W iOS, jeśli użytkownik przesunie aplikację z przełącznika aplikacji, musi ją ręcznie otworzyć ponownie, aby wiadomości w tle zaczęły znowu działać.
- W Androidzie, jeśli użytkownik wymusi zamknięcie aplikacji w ustawieniach urządzenia, musi ją ręcznie otworzyć ponownie, aby wiadomości zaczęły znowu działać.
- W sieci musisz poprosić o token (za pomocą
getToken()) z certyfikatem powiadomień push.
Prośba o zgodę na odbieranie wiadomości
W iOS, macOS, sieci i Androidzie 13 (lub nowszym) przed odebraniem ładunków FCM na urządzeniu musisz najpierw poprosić o zgodę użytkownika.
Pakiet firebase_messaging udostępnia interfejs API do wysyłania prośby o zgodę za pomocą
metody
requestPermission. Ten interfejs API akceptuje kilka nazwanych argumentów, które określają typ uprawnień, o które chcesz poprosić, np. czy przesyłanie wiadomości zawierające ładunki powiadomień może wywoływać dźwięk lub odczytywać wiadomości za pomocą Siri. Domyślnie metoda prosi o rozsądne uprawnienia domyślne. Dokumentacja interfejsu API zawiera pełne informacje o tym, do czego służy każde uprawnienie.
Aby rozpocząć, wywołaj metodę z aplikacji (w iOS wyświetli się wbudowany modal, a w sieci zostanie wywołany 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óconego przez żądanie może służyć do określenia ogólnej decyzji użytkownika:
authorized: użytkownik przyznał uprawnienia.denied: użytkownik odmówił przyznania uprawnień.notDetermined: użytkownik nie zdecydował jeszcze, czy przyznać uprawnienia.provisional: użytkownik przyznał tymczasowe uprawnienia.
Pozostałe właściwości NotificationSettings zwracają informację, czy dane uprawnienie jest włączone, wyłączone lub nieobsługiwane na bieżącym urządzeniu.
Gdy uprawnienia zostaną przyznane, a różne typy stanu urządzenia zostaną zrozumiane, 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:
Wiadomości na pierwszym planie
Aby obsługiwać wiadomości, gdy aplikacja jest 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 RemoteMessage z różnymi informacjami o ładunku, takimi jak źródło, unikalny identyfikator, czas wysłania, czy zawierał powiadomienie itp. Ponieważ wiadomość została pobrana, gdy aplikacja jest na pierwszym planie, możesz bezpośrednio uzyskać dostęp do stanu i kontekstu aplikacji Flutter.
Wiadomości na pierwszym planie i powiadomienia
Wiadomości z powiadomieniami, które docierają, gdy aplikacja jest na pierwszym planie, domyślnie nie wyświetlają widocznego powiadomienia ani w Androidzie, ani w iOS. Można jednak zastąpić to działanie:
- W Androidzie musisz utworzyć kanał powiadomień o wysokim priorytecie.
- W iOS możesz zaktualizować opcje prezentacji aplikacji.
Wiadomości w tle
Proces obsługi wiadomości w tle różni się w zależności od platformy (Android, Apple i sieć).
Platformy Apple i Android
Obsługuj wiadomości w tle, rejestrując moduł obsługi onBackgroundMessage. Gdy otrzymywane są wiadomości, tworzony jest izolowany proces (tylko w Androidzie, iOS/macOS nie wymagają osobnego izolowanego procesu), co umożliwia obsługę wiadomości nawet wtedy, gdy aplikacja nie jest uruchomiona.
Oto kilka kwestii, o których warto pamiętać w przypadku 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 inicjowania).
- 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 usuwania nieużywanych elementów w trybie wydania ).
@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 w osobnym izolowanym procesie poza kontekstem aplikacji, nie można aktualizować stanu aplikacji ani wykonywać żadnej logiki wpływającej na interfejs. Możesz jednak wykonywać logikę, taką jak żądania HTTP, operacje wejścia/wyjścia (np. aktualizowanie pamięci lokalnej), komunikować się z innymi wtyczkami itp.
Zalecamy też jak najszybsze wykonanie logiki. Długotrwałe, intensywne zadania wpływają na wydajność urządzenia i mogą spowodować zakończenie procesu przez system operacyjny. Jeśli zadania trwają dłużej niż 30 sekund, urządzenie może automatycznie zakończyć proces.
Sieć
W sieci napisz skrypt JavaScript Service Worker który działa w tle. Użyj skryptu Service Worker do obsługi wiadomości w tle.
Aby rozpocząć, 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 należy zarejestrować skrypt Service Worker. 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 Service Worker, 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 Service Worker zostanie zarejestrowany, a wszystkie wiadomości w tle będą obsługiwane za pomocą tego pliku.
Obsługa interakcji
Ponieważ powiadomienia są widocznym sygnałem, użytkownicy często wchodzą z nimi w interakcję (naciskając je). Domyślnie zarówno w Androidzie, jak i w iOS aplikacja jest otwierana. Jeśli aplikacja jest 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ługiwać 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ętego, zostanie zwróconyFuturezawierającyRemoteMessage. Po użyciuRemoteMessagezostanie usunięty.onMessageOpenedApp:Stream, który publikujeRemoteMessage, gdy aplikacja zostanie otwarta ze stanu w tle.
Zalecamy obsługę obu scenariuszy, aby zapewnić użytkownikom płynną obsługę. Poniższy przykład kodu 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 przedstawia podstawową ilustrację z użyciem StatefulWidget.
Lokalizowanie wiadomości
Zlokalizowane ciągi znaków możesz wysyłać na 2 sposoby:
- Przechowuj preferowany język każdego użytkownika na serwerze i wysyłaj dostosowane powiadomienia w każdym języku.
- Osadź zlokalizowane ciągi znaków w aplikacji i korzystaj z wbudowanych ustawień regionalnych systemu operacyjnego.
Oto jak używać 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 wiadomości 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,messageibody, użyjtitle_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 wiadomości 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
Aby przeprowadzić dalszą analizę, możesz wyeksportować dane wiadomości do BigQuery. BigQuery umożliwia analizowanie danych za pomocą BigQuery SQL, eksportowanie ich do innego dostawcy usług w chmurze lub używanie ich w niestandardowych modelach ML. Eksport do BigQuery obejmuje wszystkie dostępne dane wiadomości, niezależnie od ich typu i od tego, czy zostały wysłane za pomocą interfejsu API, czy kompozytora powiadomień.
Aby włączyć eksport, najpierw wykonaj czynności opisane w dokumencie Eksportowanie danych do BigQuery. Programowe włączenie eksportu na poziomie instancji aplikacji umożliwia poproszenie użytkowników o zgodę na analizowanie danych o dostarczaniu wiadomości (zalecane). Aby programowo włączyć eksport:
Android
Możesz użyć tego kodu:
await FirebaseMessaging.instance.setDeliveryMetricsExportToBigQuery(true);
iOS
W przypadku iOS musisz zmienić plik AppDelegate.m na tę zawartość.
#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 sieci musisz zmienić skrypt Service Worker, aby używać pakietu SDK w wersji 9. Wersja 9 musi być spakowana, więc aby skrypt Service Worker działał, musisz użyć narzędzia do pakowania, takiego jak esbuild. Zobacz przykładową
aplikację
, aby dowiedzieć się, jak to zrobić.
Po migracji do pakietu SDK w wersji 9 możesz użyć tego kodu:
import {
experimentalSetDeliveryMetricsExportedToBigQueryEnabled,
getMessaging,
} from 'firebase/messaging/sw';
...
const messaging = getMessaging(app);
experimentalSetDeliveryMetricsExportedToBigQueryEnabled(messaging, true);
Nie zapomnij uruchomić polecenia yarn build, aby wyeksportować nową wersję skryptu Service Worker do folderu web.
Wyświetlanie obrazów w powiadomieniach w iOS
Aby przychodzące powiadomienia FCM wyświetlały obrazy z ładunku FCM na urządzeniach Apple, musisz dodać dodatkowe rozszerzenie usługi powiadomień i skonfigurować aplikację tak, aby z niego korzystała.
Jeśli używasz uwierzytelniania telefonicznego Firebase, musisz dodać do pliku Podfile pod Firebase Auth.
Krok 1. Dodaj rozszerzenie usługi powiadomień
- W Xcode kliknij File > New > Target... (Plik > Nowy > Cel).
- W oknie modalnym pojawi się lista możliwych celów. Przewiń lub użyj filtra, aby wybrać Notification Service Extension (Rozszerzenie usługi powiadomień). Kliknij Next (Dalej).
- Dodaj nazwę produktu (aby postępować zgodnie z tym samouczkiem, użyj nazwy „ImageNotification”), wybierz
SwiftlubObjective-Ci kliknij Finish (Zakończ). - Włącz schemat, klikając Activate (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 Navigatorze dodaj pakiet SDK Firebase na platformy Apple: File > Add Package Dependencies... (Plik > Dodaj zależności pakietu).
Wyszukaj lub wpisz adres URL pakietu:
none https://github.com/firebase/firebase-ios-sdkDodaj do projektu
Runner: Add Package (Dodaj pakiet).Wybierz FirebaseMessaging i dodaj do celu ImageNotification: Add Package (Dodaj pakiet).
Objective-C
Upewnij się, że nowe rozszerzenie ma dostęp do podu Firebase/Messaging, dodając go do pliku Podfile:
W Navigatorze otwórz plik Podfile: Pods > Podfile (Pody > 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 installw kataloguioslubmacos.
Krok 3. Użyj pomocnika rozszerzenia
Na tym etapie wszystko powinno działać normalnie. Ostatnim krokiem jest wywołanie pomocnika rozszerzenia.
Swift
W Navigatorze 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 Navigatorze wybierz rozszerzenie ImageNotification.
Otwórz plik
NotificationService.m.U góry pliku zaimportuj
FirebaseMessaging.hbezpośrednio 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
W ładunku powiadomienia możesz teraz dodać obraz. Więcej informacji znajdziesz w artykule o tworzeniu żądania wysłania.