MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Flutter平台差异的推送通知:处理iOS与Android的推送机制

2023-07-057.1k 阅读

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推送

  1. 证书配置
    • 开发者需要在苹果开发者账号中创建推送通知证书。在 Certificates, Identifiers & Profiles 页面,创建一个 iOS Push Notification 类型的证书。此证书用于服务器与APNs之间的加密通信。
    • 将生成的证书下载并导入到本地开发环境的钥匙串访问(Keychain Access)中。
  2. Xcode项目配置
    • 在Xcode项目中,打开 Capabilities 选项卡,启用 Push Notifications 功能。这一步会自动配置项目的 Entitlements 文件,确保应用有权限接收推送通知。
    • Info.plist 文件中添加 APS - Environment 键,用于指定应用运行在开发环境还是生产环境。如果是开发环境,值设为 development;生产环境则设为 production

2.3 Flutter中处理iOS推送

  1. 依赖添加 在Flutter项目的 pubspec.yaml 文件中添加 firebase_messaging 依赖:
dependencies:
  firebase_messaging: ^12.0.0

然后运行 flutter pub get 下载依赖。

  1. 初始化配置 在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 方法请求用户授权推送通知权限。

  1. 接收推送消息 在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推送

  1. 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'
}
  1. 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推送

  1. 依赖与初始化 与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 自动初始化。

  1. 接收推送消息 在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'),
        ),
      ),
    );
  }
}

四、平台差异处理

  1. 通知样式差异
    • iOS:iOS的推送通知样式相对简洁。在Flutter中,可以通过 RemoteNotification 的属性来设置标题、正文等基本信息。例如:
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) {
    // 处理大图片
  }
}
  1. 通知优先级差异
    • iOS:iOS的推送通知优先级通过 apns - priority 字段设置,取值范围为0 - 10。10表示最高优先级,会立即推送;5表示较低优先级,可能会延迟推送。在 firebase_messaging 插件中,可以通过设置 RemoteMessageapns 字段来配置:
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'
  )
);
  1. 后台运行处理差异
    • iOS:在iOS中,应用处于后台时,推送通知由系统接管并显示。应用可以在 AppDelegate 中通过 didReceiveRemoteNotification:fetchCompletionHandler: 方法处理后台推送消息。在Flutter中,firebase_messaging 插件会自动处理部分逻辑,但如果需要自定义处理,可以通过 MethodChannel 与原生代码交互。
    • Android:Android应用在后台时,同样可以接收推送通知。不过,由于Android系统的内存管理机制,应用可能会被系统杀死。为了确保推送消息的接收,开发者可以使用Android的JobScheduler或WorkManager来定期唤醒应用检查推送。在Flutter中,firebase_messaging 插件在一定程度上可以处理后台推送,但对于更复杂的场景,可能需要结合原生代码进行优化。

五、测试推送通知

  1. 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} 进行测试。
  2. 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服务器密钥。

六、常见问题与解决方法

  1. 推送消息丢失
    • 原因:网络问题、设备离线时间过长、APNs或FCM服务器故障等都可能导致推送消息丢失。
    • 解决方法:在应用服务器端实现消息重试机制,当发送推送消息失败时,按照一定的策略(如指数退避算法)进行重试。同时,在设备端可以通过监听网络变化,当网络恢复时主动请求推送消息。
  2. 通知不显示
    • 原因:可能是权限问题、通知配置错误或应用在后台被系统限制。
    • 解决方法:检查应用是否正确获取推送通知权限,在iOS中可以引导用户到设置页面开启权限;在Android中可以通过 Settings.ACTION_APPLICATION_DETAILS_SETTINGS 意图跳转到应用设置页面。检查通知的配置,确保标题、正文等信息正确设置。对于Android后台限制问题,可以优化应用的电池优化设置,避免应用被过度限制。
  3. 设备令牌更新
    • 原因:在iOS中,设备令牌可能会在系统更新、应用重新安装等情况下发生变化;在Android中,注册令牌也可能会不定期更新。
    • 解决方法:在Flutter应用中,通过监听 FirebaseMessaging.instance.onTokenRefresh 事件,当设备令牌或注册令牌更新时,及时将新的令牌发送给应用服务器。
FirebaseMessaging.instance.onTokenRefresh.listen((token) {
  print('New Device Token: $token');
  // 将新token发送到应用服务器
});

通过深入理解iOS和Android的推送机制,并针对平台差异进行细致的配置和处理,开发者可以在Flutter应用中实现高效、稳定的推送通知功能,提升用户体验。同时,在开发过程中注意测试和常见问题的解决,确保推送通知在各种场景下都能正常工作。