Flutter平台差异的推送通知:处理iOS与Android的推送机制
Flutter平台差异的推送通知:处理iOS与Android的推送机制
一、Flutter推送通知概述
在移动应用开发中,推送通知是与用户保持互动的重要手段。Flutter作为跨平台开发框架,为开发者提供了统一的接口来处理推送通知,但由于iOS和Android系统底层推送机制的不同,开发者需要了解并处理这些差异,以确保推送通知在两个平台上都能正常、高效地工作。
Flutter中处理推送通知主要依赖于第三方插件,如 firebase_messaging
插件,它基于Firebase Cloud Messaging(FCM)服务,为iOS和Android提供了统一的推送解决方案。不过,即使使用这样的统一方案,开发者仍需要针对平台差异进行一些额外的配置和处理。
二、iOS推送机制
2.1 iOS推送架构
iOS推送通知基于苹果推送通知服务(APNs,Apple Push Notification service)。APNs充当应用服务器和iOS设备之间的桥梁。当应用服务器有推送消息要发送时,它首先将消息发送到APNs,APNs再将消息推送给目标iOS设备上的应用。
在iOS设备端,应用需要向系统注册推送通知权限。用户授权后,系统会为应用生成一个唯一的设备令牌(device token),应用将这个设备令牌发送给应用服务器,以便服务器能够准确地向该设备推送消息。
2.2 配置iOS推送
- 证书配置
- 开发者需要在苹果开发者账号中创建推送通知证书。在
Certificates, Identifiers & Profiles
页面,创建一个iOS Push Notification
类型的证书。此证书用于服务器与APNs之间的加密通信。 - 将生成的证书下载并导入到本地开发环境的钥匙串访问(Keychain Access)中。
- 开发者需要在苹果开发者账号中创建推送通知证书。在
- Xcode项目配置
- 在Xcode项目中,打开
Capabilities
选项卡,启用Push Notifications
功能。这一步会自动配置项目的Entitlements
文件,确保应用有权限接收推送通知。 - 在
Info.plist
文件中添加APS - Environment
键,用于指定应用运行在开发环境还是生产环境。如果是开发环境,值设为development
;生产环境则设为production
。
- 在Xcode项目中,打开
2.3 Flutter中处理iOS推送
- 依赖添加
在Flutter项目的
pubspec.yaml
文件中添加firebase_messaging
依赖:
dependencies:
firebase_messaging: ^12.0.0
然后运行 flutter pub get
下载依赖。
- 初始化配置
在Flutter应用的入口文件(通常是
main.dart
)中初始化FirebaseMessaging
:
import 'package:firebase_messaging/firebase_messaging.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await FirebaseMessaging.instance.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
runApp(MyApp());
}
这里通过 requestPermission
方法请求用户授权推送通知权限。
- 接收推送消息 在Flutter应用中,可以通过以下方式接收推送消息:
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
@override
void initState() {
super.initState();
_firebaseMessaging.getToken().then((token) {
print('Device Token: $token');
// 将token发送到应用服务器
});
_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}');
}
});
_firebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
print('A new onMessageOpenedApp event was published!');
print('Message data: ${message.data}');
if (message.notification != null) {
print('Message also contained a notification: ${message.notification}');
}
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('iOS Push Example'),
),
body: Center(
child: Text('iOS Push Notification Example'),
),
),
);
}
}
getToken
方法用于获取设备令牌,onMessage
回调在应用处于前台时接收推送消息,onMessageOpenedApp
回调在用户点击推送通知打开应用时触发。
三、Android推送机制
3.1 Android推送架构
Android推送通知基于Google Cloud Messaging(GCM),后来升级为Firebase Cloud Messaging(FCM)。FCM为开发者提供了可靠且可扩展的推送解决方案。与iOS类似,应用服务器将推送消息发送到FCM服务器,FCM再将消息推送给目标Android设备上的应用。
在Android设备端,应用同样需要获取推送通知权限。设备会向FCM服务器注册,获取一个注册令牌(registration token),应用将此令牌发送给应用服务器,以便服务器进行推送。
3.2 配置Android推送
- Google服务配置
- 在
build.gradle
文件中添加Google服务插件依赖:
- 在
buildscript {
dependencies {
classpath 'com.google.gms:google - services:4.3.10'
}
}
- 在 `app/build.gradle` 文件中应用Google服务插件,并添加 `firebase - messaging` 依赖:
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google - services'
dependencies {
implementation 'com.google.firebase:firebase - messaging:23.0.0'
}
- AndroidManifest.xml配置
在
AndroidManifest.xml
文件中添加必要的权限和接收器:
<uses - permission android:name="android.permission.INTERNET" />
<uses - permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<permission
android:name="${applicationId}.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses - permission android:name="${applicationId}.permission.C2D_MESSAGE" />
<receiver
android:name="com.google.firebase.messaging.FirebaseMessagingReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND">
<intent - filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="${applicationId}" />
</intent - filter>
</receiver>
<receiver
android:name="com.google.firebase.messaging.FirebaseInstanceIdReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND">
<intent - filter>
<action android:name="com.google.android.gms.iid.InstanceID" />
<category android:name="${applicationId}" />
</intent - filter>
</receiver>
3.3 Flutter中处理Android推送
- 依赖与初始化
与iOS类似,在
pubspec.yaml
文件中添加firebase_messaging
依赖并下载。在main.dart
中初始化FirebaseMessaging
:
import 'package:firebase_messaging/firebase_messaging.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await FirebaseMessaging.instance.setAutoInitEnabled(true);
runApp(MyApp());
}
setAutoInitEnabled
方法确保 FirebaseMessaging
自动初始化。
- 接收推送消息 在Flutter应用中接收Android推送消息的代码与iOS部分类似:
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
@override
void initState() {
super.initState();
_firebaseMessaging.getToken().then((token) {
print('Device Token: $token');
// 将token发送到应用服务器
});
_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}');
}
});
_firebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
print('A new onMessageOpenedApp event was published!');
print('Message data: ${message.data}');
if (message.notification != null) {
print('Message also contained a notification: ${message.notification}');
}
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Android Push Example'),
),
body: Center(
child: Text('Android Push Notification Example'),
),
),
);
}
}
四、平台差异处理
- 通知样式差异
- iOS:iOS的推送通知样式相对简洁。在Flutter中,可以通过
RemoteNotification
的属性来设置标题、正文等基本信息。例如:
- iOS:iOS的推送通知样式相对简洁。在Flutter中,可以通过
if (message.notification != null) {
print('iOS Notification title: ${message.notification.title}');
print('iOS Notification body: ${message.notification.body}');
}
- **Android**:Android支持更丰富的通知样式,如大图片样式、进度条样式等。可以使用 `android` 命名空间下的属性来定制。例如,设置大图片样式:
if (message.notification != null && message.notification.android != null) {
AndroidNotification androidNotification = message.notification.android;
if (androidNotification.bigPicture != null) {
// 处理大图片
}
}
- 通知优先级差异
- iOS:iOS的推送通知优先级通过
apns - priority
字段设置,取值范围为0 - 10。10表示最高优先级,会立即推送;5表示较低优先级,可能会延迟推送。在firebase_messaging
插件中,可以通过设置RemoteMessage
的apns
字段来配置:
- iOS:iOS的推送通知优先级通过
RemoteMessage message = RemoteMessage(
apns: ApnsConfig(
headers: {
'apns - priority': '10'
}
)
);
- **Android**:Android的推送通知优先级通过 `priority` 字段设置,取值为 `high` 或 `low`。`high` 优先级会立即唤醒设备并显示通知,`low` 优先级则可能在设备空闲时推送。在 `firebase_messaging` 插件中,可以通过设置 `RemoteMessage` 的 `android` 字段来配置:
RemoteMessage message = RemoteMessage(
android: AndroidConfig(
priority: 'high'
)
);
- 后台运行处理差异
- iOS:在iOS中,应用处于后台时,推送通知由系统接管并显示。应用可以在
AppDelegate
中通过didReceiveRemoteNotification:fetchCompletionHandler:
方法处理后台推送消息。在Flutter中,firebase_messaging
插件会自动处理部分逻辑,但如果需要自定义处理,可以通过MethodChannel
与原生代码交互。 - Android:Android应用在后台时,同样可以接收推送通知。不过,由于Android系统的内存管理机制,应用可能会被系统杀死。为了确保推送消息的接收,开发者可以使用Android的JobScheduler或WorkManager来定期唤醒应用检查推送。在Flutter中,
firebase_messaging
插件在一定程度上可以处理后台推送,但对于更复杂的场景,可能需要结合原生代码进行优化。
- iOS:在iOS中,应用处于后台时,推送通知由系统接管并显示。应用可以在
五、测试推送通知
- iOS测试
- 开发环境:可以使用Xcode的模拟器或真机进行测试。在应用中获取设备令牌后,将其复制到测试工具(如Postman)中,构造APNs请求发送推送消息。请求URL为
https://api.development.push.apple.com/3/device/{device - token}
,请求头中添加Authorization: bearer {your - apns - auth - token}
,请求体为推送消息内容。 - 生产环境:需要使用生产环境的APNs证书和URL
https://api.push.apple.com/3/device/{device - token}
进行测试。
- 开发环境:可以使用Xcode的模拟器或真机进行测试。在应用中获取设备令牌后,将其复制到测试工具(如Postman)中,构造APNs请求发送推送消息。请求URL为
- Android测试
- 开发环境:可以使用Android模拟器或真机进行测试。在应用中获取注册令牌后,通过FCM的HTTP v1 API发送推送消息。请求URL为
https://fcm.googleapis.com/v1/projects/{your - project - id}/messages:send
,请求头中添加Authorization: Bearer {your - firebase - server - key}
,请求体为推送消息内容。 - 生产环境:同样使用FCM的HTTP v1 API,确保使用的是生产环境的Firebase服务器密钥。
- 开发环境:可以使用Android模拟器或真机进行测试。在应用中获取注册令牌后,通过FCM的HTTP v1 API发送推送消息。请求URL为
六、常见问题与解决方法
- 推送消息丢失
- 原因:网络问题、设备离线时间过长、APNs或FCM服务器故障等都可能导致推送消息丢失。
- 解决方法:在应用服务器端实现消息重试机制,当发送推送消息失败时,按照一定的策略(如指数退避算法)进行重试。同时,在设备端可以通过监听网络变化,当网络恢复时主动请求推送消息。
- 通知不显示
- 原因:可能是权限问题、通知配置错误或应用在后台被系统限制。
- 解决方法:检查应用是否正确获取推送通知权限,在iOS中可以引导用户到设置页面开启权限;在Android中可以通过
Settings.ACTION_APPLICATION_DETAILS_SETTINGS
意图跳转到应用设置页面。检查通知的配置,确保标题、正文等信息正确设置。对于Android后台限制问题,可以优化应用的电池优化设置,避免应用被过度限制。
- 设备令牌更新
- 原因:在iOS中,设备令牌可能会在系统更新、应用重新安装等情况下发生变化;在Android中,注册令牌也可能会不定期更新。
- 解决方法:在Flutter应用中,通过监听
FirebaseMessaging.instance.onTokenRefresh
事件,当设备令牌或注册令牌更新时,及时将新的令牌发送给应用服务器。
FirebaseMessaging.instance.onTokenRefresh.listen((token) {
print('New Device Token: $token');
// 将新token发送到应用服务器
});
通过深入理解iOS和Android的推送机制,并针对平台差异进行细致的配置和处理,开发者可以在Flutter应用中实现高效、稳定的推送通知功能,提升用户体验。同时,在开发过程中注意测试和常见问题的解决,确保推送通知在各种场景下都能正常工作。