| Wybierz platformę: | iOS+ Android Web Flutter Unity C++ |
W zależności od stanu urządzenia wiadomości przychodzące są obsługiwane inaczej. Aby zrozumieć te scenariusze i sposób integracji FCM z własną aplikacją, musisz najpierw poznać różne stany, w jakich może znajdować się urządzenie:
| Stan | Opis |
|---|---|
| Pierwszy plan | Gdy aplikacja jest otwarta, widoczna i używana. |
| Tło | Gdy 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 internecie). |
| Zakończona | Gdy 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 internecie musisz poprosić o token (za pomocą
getToken()) z certyfikatem push w internecie.
Prośba o zgodę na odbieranie wiadomości
W iOS, macOS, internecie 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 internecie 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 określone uprawnienie jest włączone, wyłączone lub nieobsługiwane na bieżącym urządzeniu.
Gdy uprawnienia zostaną przyznane i zrozumiesz różne typy stanów 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:
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 na 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 internet).
Platformy Apple i Android
Obsługuj wiadomości w tle, rejestrując obsługę onBackgroundMessage. Gdy wiadomości są odbierane, tworzony jest izolowany proces (tylko w Androidzie, iOS/macOS nie wymaga 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 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, obsługę wiadomości należy
oznaczyć adnotacją
@pragma('vm:entry-point')bezpośrednio nad deklaracją funkcji (w przeciwnym razie może zostać usunięta podczas usuwania nieużywanego kodu 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ż obsługa 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 internecie 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 obsługi wiadomości, inicjować Firebase i udostępniać zmienną messaging.
Następnie należy zarejestrować skrypt. W pliku index.html zarejestruj
skrypt, 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ą widocznym sygnałem, dlatego użytkownicy często wchodzą z nimi w interakcję (klikając je). Domyślnie zarówno w Androidzie, jak i w iOS otwiera się aplikacja. 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 kliknie, możesz chcieć 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 dostarczania 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 typu wiadomości i tego, czy wiadomość została wysłana 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 dostarczania 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 internetu musisz zmienić skrypt Service Worker, aby używać pakietu SDK w wersji 9. Wersja 9 musi być spakowana, dlatego do działania skryptu Service Worker 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 pakiet Firebase Auth.
Krok 1. Dodaj rozszerzenie usługi powiadomień
- W Xcode kliknij File > New > Target... (Plik > Nowy > Cel).
- Wyświetli się modal z listą 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 dla platform Apple: File > Add Package Dependencies...
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 pakietu Firebase/Messaging, dodając go do pliku Podfile:
W Navigatorze otwórz plik Podfile: Pods > Podfile (Pakiety > 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 pakiety za pomocą polecenia
pod installw kataloguioslubmacos.
Krok 3. Użyj narzędzia pomocniczego rozszerzenia
Na tym etapie wszystko powinno działać normalnie. Ostatnim krokiem jest wywołanie narzędzia pomocniczego 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.