No results found
We couldn't find anything using that term, please try searching for something else.
收到的消息的处理方式因设备状态而异。如需了解这些情形以及如何将 FCM 集成到您自己的应用中,必须先确定设备可能会处于的各种状态: 状态 说明 前台 当应用处于打开、查看和使用状态时。 后台 当应用处于打开状态但在后台运行(最小化)时。通常,当用户按设
收到的消息的处理方式因设备状态而异。如需了解这些情形以及如何将 FCM 集成到您自己的应用中,必须先确定设备可能会处于的各种状态:
状态 | 说明 |
---|---|
前台 | 当应用处于打开、查看和使用状态时。 |
后台 | 当应用处于打开状态但在后台运行(最小化)时。通常,当用户按设备上的“主屏幕”按钮、使用应用切换器切换到其他应用或在其他标签页 (web) 中打开应用时,就会发生这种情况。 |
已终止 | 当设备已锁定或应用未运行时。 |
在应用能够通过 FCM 接收消息载荷之前,必须满足一些前提条件 :
getToken( )
)。在iOS、macOS、web 和Android 13(或更高版本)上,您必须先获得用户的许可,才能在设备上接收 FCM 载荷。
firebase_message
软件包提供了一个简单的 API,用于通过 requestPermission
方法请求权限。此 API 会接受多个命名参数,这些参数定义了您要请求的权限类型,例如包含通知载荷的消息功能是否可以触发声音或通过 Siri 读出消息。默认情况下,该方法会请求合理的默认权限。参考 API 提供了有关每项权限用途的完整文档。
如需开始请求权限,请从您的应用调用该方法(在ios 上,系统将显示原生模态;在web 上,系统将触发浏览器的原生 API 流程 ) :
firebasemessage message = firebasemessage.instance;
NotificationSettings setting = await message.requestPermission(
alert: true,
announcement: false,
badge : true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
) ;
print('User granted permission: $ {setting.authorizationStatus}') ;
从请求返回的 NotificationSettings
对象的 authorizationStatus
属性可用于确定用户的总体决定:
authorized
:用户授予了权限。deny
:用户拒绝了权限。notdetermine
:用户尚未选择是否要授予权限。provisional
:用户授予了临时权限。 注意:在Android 13 之前的版本中,如果用户未在操作系统设置中停用应用的通知,则 authorizationStatus
会返回authorized
。在Android 13 及更高版本中,无法确定用户是否已选择授予/拒绝权限。deny
值表示未确定或已拒绝的权限状态,您需要自行跟踪是否已发出权限请求 。
NotificationSettings
中的其他属性会返回关于当前设备是已启用、已停用还是不支持特定权限的信息 。
获得权限并了解不同类型的设备状态后,您的应用现在便可以开始处理传入的 FCM 载荷了。
根据应用的当前状态,不同消息类型的传入载荷需要不同的实现来处理:
如需在应用在前台运行的情况下处理消息,请监听 onmessage
流 。
firebasemessage.onmessage.listen( (remotemessage message) {
print(' got a message whilst in the foreground ! ') ;
print('Message data: $ {message.data}') ;
if (message.notification ! = null) {
print(' Message is contained also contain a notification :$ {message.notification}') ;
}
}) ;
该流包含一个 remotemessage
, 其中详细说明了有关载荷的各种信息,例如载荷的来源、唯一 ID、发送时间、载荷是否包含通知等等。由于消息是应用在前台运行时检索的,因此您可以直接访问 Flutter 应用的状态和上下文 。
默认情况下,应用在前台运行时送达的通知消息不会在Android 和ios 上显示可见的通知。不过,您可以替换此行为 :
在原生(Android 和Apple)和基于 web 的平台上,处理后台消息的过程有所不同 。
通过注册onbackgroundmessage
处理程序来处理后台消息。收到消息后,系统会生成一个隔离环境(仅限 Android,iOS/macOS 不需要单独的隔离环境),这样一来,即使您的应用未运行,您也可以处理消息。
关于后台消息处理程序,您需要注意以下几点:
@pragma('vm:entry-point')
标注消息处理程序(否则,对于发布模式,可能会在摇树优化期间将其移除)。@pragma('vm:entry-point')
Future<void> _ firebasemessagebackgroundhandler(remotemessage message) async {
// If you 're go to use other Firebase service in the background , such as Firestore ,
// make sure you call ` initializeapp ` before using other Firebase service .
await Firebase.initializeApp( ) ;
print(" handle a background message :$ {message.messageId}") ;
}
void main( ) {
firebasemessage.onbackgroundmessage(_ firebasemessagebackgroundhandler) ;
runApp(MyApp( )) ;
}
由于处理程序在应用上下文之外的自有隔离环境中运行,因此无法更新应用状态或执行任何影响逻辑的界面。但是,您可以执行 HTTP 请求之类的逻辑、执行 IO 操作(例如更新本地存储空间)、与其他插件通信,等等。
我们还建议您尽快完成自己的逻辑。运行耗时较长的密集型任务会影响设备性能,并且可能导致操作系统终止进程。如果任务运行时间超过 30 秒,设备可能就会自动终止进程。
在web 上,编写一个在后台运行的 JavaScript Service Worker。使用 Service Worker 处理后台消息。
首先,在web
目录中创建一个新文件,并将其命名为 firebase-message-sw.js
:
// Please see this file for the latest firebase-js-sdk version:
// https://github.com/firebase/flutterfire/blob/master/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-message-compat.js") ;
firebase.initializeApp( {
apiKey: " ... ",
authDomain: " ... ",
databaseURL: " ... ",
projectId: " ... ",
storagebucket: " ... ",
messageSenderId: " ... ",
appid: " ... ",
}) ;
const message = firebase.message( ) ;
// Optional:
message.onbackgroundmessage( (message) => {
console.log("onbackgroundmessage", message) ;
}) ;
该文件必须导入应用和消息传递 SDK,初始化 Firebase 并公开 message
变量。
接下来,必须注册 Service Worker。在index.html
文件中,通过修改用于引导 Flutter 的<script>
标记来注册 Worker :
<script src="flutter_bootstrap.js" async>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function ( ) {
navigator.serviceWorker.register('firebase-message-sw.js', {
scope: '/firebase-cloud-message-push-scope',
}) ;
}) ;
}
</script>
如果您仍在使用旧的模板系统,可以通过修改用于引导 Flutter 的<script>
标记来注册 Worker,如下所示:
<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-message-sw.js', {
scope: '/firebase-cloud-message-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>
接下来,重启您的 is Flutter flutter 应用。系统将注册 Service Worker,并通过此文件处理任何后台消息 。
由于通知是一种可见的提示,因此用户常常会通过点按来与之交互。android 和ios 上的默认行为均是打开应用。也就是说,如果应用当前是终止状态,系统便会启动应用;如果应用在后台运行,系统则会将其转至前台 。
根据通知的具体内容,您可能会希望在应用打开时便处理用户与通知的交互。例如,如果系统通过通知发送了新的聊天消息并且用户点按了该消息,那么您可能会希望应用在打开时便同时打开具体对话内容。
firebase-message
软件包提供了两种方式来处理此类交互 :
getInitialMessage( )
: 如果应用在打开之前处于终止状态,系统将返回一个包含remotemessage
的Future
;并且系统会在用户使用该 remotemessage
之后将其移除。onmessageOpenedApp
:如果应用在打开之前处于后台状态,则系统会通过一个 stream
来发布 remotemessage
。建议对这两种情况都予以处理,以确保为用户提供顺畅的用户体验。以下代码示例简单展示了实现上述操作的方法 :
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 firebasemessage.instance.getInitialMessage( ) ;
// If the message is contains also contain a datum 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
firebasemessage.onmessageOpenedApp.listen(_handleMessage) ;
}
void _handleMessage(remotemessage message) {
if (message.data[' type '] == ' chat ') {
Navigator.pushname(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-
目录中指定经过翻译处理的消息。例如,在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"
}
}
在base.lproj/localizable.string
中指定采用默认语言表示的消息 :
"NOTIFICATION_TITLE" = "Hello World";
"NOTIFICATION_MESSAGE" = "This is a message";
在.lproj
目录中指定经过翻译处理的消息。例如,在fr.lproj/localizable.string
中指定采用法语表示的消息:
" 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 发送还是通过 Notifications Composer 发送。
如需启用导出功能,请先点击此链接按照相关步骤操作,然后按照以下说明操作:
您可以使用以下代码 :
await firebasemessage.instance.setDeliveryMetricsExportToBigQuery(true) ;
对于 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
对于 web,您需要更改 Service Worker,才能使用 v9 版本的 SDK。v9 版本需要捆绑使用,因此您需要先使用捆绑器(如 esbuild
) 让 Service Worker 能够正常工作。请参阅示例应用,了解如何执行此操作 。
迁移到 v9 SDK 后,您可以使用以下代码 :
import {
experimentalSetDeliveryMetricsExportedToBigQueryEnabled,
getMessaging,
} from 'firebase/message/sw';
...
const message = getMessaging(app) ;
experimentalSetDeliveryMetricsExportedToBigQueryEnabled(message, true) ;
不要忘记运行 yarn is build build
,以将新版 Service Worker 导出到 web
文件夹 。
在Apple 设备上,为了让传入的 FCM 通知显示来自 FCM 载荷的图片,您必须添加额外的通知服务扩展程序,并将您的应用配置为使用该扩展程序。
如果您使用的是 Firebase 手机身份验证,则必须将 Firebase Auth pod 添加到您的 Podfile 中。
将新扩展程序添加到 Podfile 中,确保新扩展程序能够访问Firebase/Messaging
pod:
从导航器中,打开 Podfile:Pod > Podfile
向下滚动到文件底部,然后添加以下内容:
target 'ImageNotification' do
use_frameworks!
pod ' Firebase / Auth ' # Add this line if you are using FirebaseAuth phone authentication
pod 'Firebase/Messaging'
end
使用 ios
或 macos
目录中的pod is install install
安装或更新 pod。
此时,一切应该仍然正常运行。最后一步是调用扩展程序帮助程序。
从导航器中,选择您的 ImageNotification 扩展程序
打开 NotificationService.m
文件。
在文件顶部,在NotificationService.h
之后导入firebasemessage.h
,如下所示。
将 NotificationService.m
的内容替换为以下内容 :
#import "NotificationService.h"
#import "firebasemessage.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 {
// call just before the extension will be terminate 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
现在,您可以在通知载荷中添加图片。请参阅关于如何构建发送请求的 iOS 文档。请注意,设备强制执行的图片大小上限为 300 KB 。