Flutter 推送冷背景處理
Flutter FCM for App Terminated?
需求:
當 App 尚未啟動時,推送一個 Silent Push 依據自訂規則決定是否顯示推送訊息在系統列
先說結論:
這在 Flutter FirebaseMessaging Plugin 下,是無法辦到的。
Flutter App 沒啟動過,休想知道任何推送內容。
首先,我們知道 Flutter 是伴隨 Activity 才啟動的, Flutter FirebaseMessaging Plugin 雖然有註冊 FirebaseMessagingService
,但是頂多能做的就是保留推送訊息,直到 Flutter App 伴隨 FirebaseMessaging Plugin 啟動時,把保留的推送訊息吐出罷了。
儘管 Flutter FirebaseMessaging Plugin 透過了 Isolate 實現了 Flutter App 在背景也能處理推送訊息,仍然無法支援尚未啟動過的 Flutter App。
FlutterFirebaseMessagingService.java:
private static void executeDartCallbackInBackgroundIsolate(...) {
...
if (remoteMessage.getData() != null) {
messageData.put("data", remoteMessage.getData());
}
if (remoteMessage.getNotification() != null) {
messageData.put("notification", remoteMessage.getNotification());
}
args.put("message", messageData);
backgroundChannel.invokeMethod("handleBackgroundMessage", args, result);
}
firebase_messaging.dart:
void _fcmSetupBackgroundChannel(...) async {
...
backgroundChannel.setMethodCallHandler((MethodCall call) async {
if (call.method == 'handleBackgroundMessage') {
...
}class FirebaseMessaging {
void configure({...}) {
...
PluginUtilities.getCallbackHandle(_fcmSetupBackgroundChannel);
...
}
}
畢竟 FirebaseMessaging.configure()
就是隨著 Activity 啟動的。
就算我們想要直接把實作寫在原生端,除了竄改 Flutter FirebaseMessaging Plugin 的 FlutterFirebaseMessagingService
也別無他法。(除非不用 FirebaseMessaging Plugin
因為 FirebaseMessagingService
只能有一個。(只有一個?
透過一個 FirebaseMessagingService 代理:
就算實現一個 FirebaseMessaagingProxyService
代理,把一些自訂實作放上後,再去拖 FlutterFirebaseMessagingService
也不甚安全的做法,因為共通的 FirebaseMessagingService
父類實作有不可預期衝突的行為,只能各自保佑了。
保險起見,記得覆蓋 com.google.firebase.MESSAGING_EVENT
Service AndroidManifest.xml:
<service android:name="io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService"
android:exported="false">
<intent-filter android:priority="-500">
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
<service android:name=".FirebaseMessagingProxyService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
FirebaseMessagingProxyService.kt:
故事還沒完…
如果本身的推送訊息同時有 notification
以及 data
則需要在 MainActivity
處理了。讓我們看這張表:
能收到的資料只放在 intent 內,這下連 FirebaseMessagingProxyService 都看不到了。
需要實現 FlutterActivityPlugin 把 intent 存著,直到 Flutter App 啟動時,轉交出去。
重點在於 FlutterActivityPlugin 如何得知 Flutter App 已經啟動?
簡單的做法就是: home_screen.dart#initState():
EventChannel('activity/intent').receiveBroadcastStream()
.listen((intent) {
print(intent);
}
FlutterActivityPlugin.java:
ActivityPlugin(PluginRegistry.Registrar registrar, @NonNull Activity activity) {
eventChannel = new EventChannel(registrar.messenger(), "activity/intent");
eventChannel.setStreamHandler(this); // onListen()
registrar.addNewIntentListener(this);
}@Override
public boolean onNewIntent(Intent intent) {
return onIntent(intent);
}
/// onCreate() => constructor
/// onNewIntent()
public boolean onIntent(Intent intent) {
if (intentSink != null) {
intentSink.success(intent);
}
return false;
}
@Override
public void onListen(Object o, EventChannel.EventSink eventSink) {
intentSink = eventSink;
// 重點在這,這可得知 Flutter App 開始聽
// 那就發 activity 目前的 intent 吧
intentSink.success(registrar.activity().getIntent());
}
這樣就基本完成推送冷背景啟動 Flutter App 仍可得知推送內容。