[Firebase 消息传递]:应用程序在后台时不调用后台消息处理程序方法?

Posted

技术标签:

【中文标题】[Firebase 消息传递]:应用程序在后台时不调用后台消息处理程序方法?【英文标题】:[Firebase Messaging]: Background Message Handler method not called when the app in background? 【发布时间】:2020-04-05 12:09:03 【问题描述】:

我正在开发一个使用 FCM 推送通知的应用程序,当应用程序在后台或终止并接收新通知时,我需要将此通知的数据保存在本地(SQLITE)中,无需单击通知或重新打开再次应用程序,除非单击,否则不会在应用程序中读取通知。有什么建议吗?

这是我的 NotificationHandler.dart

import 'dart:async';
import 'dart:io';
import 'package:eshaar/model/message.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:intl/intl.dart';
import 'package:eshaar/model/database_helper.dart';


FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
new FlutterLocalNotificationsPlugin();

Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) async 
  print("onBackgroundMessage: $message");
  //_showBigPictureNotification(message);
  return Future<void>.value();



Future onSelectNotification(String payload) async 



class NotificationHandler 
  FirebaseMessaging _fcm = FirebaseMessaging();
  StreamSubscription iosSubscription;
  DatabaseHelper db = new DatabaseHelper();
  static final NotificationHandler _singleton =
  new NotificationHandler._internal();

  factory NotificationHandler() 
    return _singleton;
  
  NotificationHandler._internal();

  initializeFcmNotification() async 
    var initializationSettingsandroid =
    new AndroidInitializationSettings('mipmap/ic_launcher');
    var initializationSettingsIOS = new IOSInitializationSettings(
        onDidReceiveLocalNotification: onDidReceiveLocalNotification);
    var initializationSettings = new InitializationSettings(
        initializationSettingsAndroid, initializationSettingsIOS);
    flutterLocalNotificationsPlugin.initialize(initializationSettings,
        onSelectNotification: onSelectNotification);

    if (Platform.isIOS) 
      iosSubscription = _fcm.onIosSettingsRegistered.listen((data) 
        // save the token  OR subscribe to a topic here
      );

      _fcm.requestNotificationPermissions(IosNotificationSettings());
     else 
      _saveDeviceToken();
    

    _fcm.configure(
      onMessage: (Map<String, dynamic> message) async 
        print("---------------------On Message--------------------");
        displayNotification(message['data']['title'],message['data']['body']);
        _saveNotificationToLocal(message);
      ,
      onBackgroundMessage: Platform.isIOS ? null : myBackgroundMessageHandler ,
      onLaunch: (Map<String, dynamic> message) async 
        print("---------------------On Lunch--------------------");
        _saveNotificationToLocal(message);
      ,
      onResume: (Map<String, dynamic> message) async 
        print("---------------------On Resume--------------------");
        _saveNotificationToLocal(message);
      ,
    );
  

  _saveNotificationToLocal(Map<String, dynamic> message)
    String sender_id = message['data']['sender_id'];
    String message_id = message['data']['message_id'];
    String title = message['data']['title'];
    String body = message['data']['body'];
    String del_time  = getCurrentDateTime();
    Messages msg = new Messages(sender_id,message_id,title,body,del_time);
    db.saveMessages(msg);

  

 String getCurrentDateTime()
   var now = new DateTime.now();
   var formatter = new DateFormat('MMM d yy h:mm a');
   String formatted = formatter.format(now);
   return formatted;
  

  /// Get the token, save it to the database for current user
  _saveDeviceToken() async 
    String fcmToken = await _fcm.getToken();
  


  Future<void> onDidReceiveLocalNotification(
      int id, String title, String body, String payload) async 
    // display a dialog with the notification details, tap ok to go to another page
  

  void displayNotification(String title,String body) async
    var androidPlatformChannelSpecifics = AndroidNotificationDetails(
        'your channel id', 'your channel name', 'your channel description',
        importance: Importance.Max, priority: Priority.High, ticker: 'ticker');
    var iOSPlatformChannelSpecifics = IOSNotificationDetails();
    var platformChannelSpecifics = NotificationDetails(
        androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
    await flutterLocalNotificationsPlugin.show(
        0, '$title', '$body', platformChannelSpecifics,
        payload: 'item x');
  

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="ot.eshaar.eshaar">
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    <uses-permission android:name="android.permission.VIBRATE" />



    <!-- io.flutter.app.FlutterApplication is an android.app.Application that
         calls FlutterMain.startInitialization(this); in its onCreate method.
         In most cases you can leave this as-is, but you if you want to provide
         additional functionality it is fine to subclass or reimplement
         FlutterApplication and put your custom class here. -->
    <application
        android:name=".Application"
        android:label="eshaar"
        android:icon="@mipmap/ic_launcher">
        <activity
            android:name=".MainActivity"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <!-- This keeps the window background of the activity showing
                 until Flutter renders its first frame. It can be removed if
                 there is no splash screen (such as the default splash screen
                 defined in @style/LaunchTheme). -->
            <meta-data
                android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
                android:value="true" />


            <meta-data
                    android:name="com.google.firebase.messaging.android.channel_id"
                    android:value="mmne" />

            <meta-data android:name="com.google.firebase.messaging.default_notification_channel_id" android:value="@string/default_notification_channel_id"/>


            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>

            <intent-filter>
                <action android:name="FLUTTER_NOTIFICATION_CLICK" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        <receiver android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"></action>
            </intent-filter>
        </receiver>
        <receiver android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver" />


    </application>
</manifest>

颤振医生-v

[✓] Flutter (Channel stable, v1.9.1+hotfix.6, on Mac OS X 10.14.4 18E226, locale
    en-US)
    • Flutter version 1.9.1+hotfix.6 at /Users/mahmoudabdelaziz/Desktop/flutter
    • Framework revision 68587a0916 (3 months ago), 2019-09-13 19:46:58 -0700
    • Engine revision b863200c37
    • Dart version 2.5.0


[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
    • Android SDK at /Users/mahmoudabdelaziz/Library/Android/sdk
    • Android NDK location not configured (optional; useful for native profiling
      support)
    • Platform android-28, build-tools 28.0.3
    • Java binary at: /Applications/Android
      Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build
      1.8.0_152-release-1343-b01)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 11.0)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 11.0, Build version 11A420a
    • CocoaPods version 1.8.4

[!] Android Studio (version 3.4)
    • Android Studio at /Applications/Android Studio.app/Contents
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
    • Java version OpenJDK Runtime Environment (build
      1.8.0_152-release-1343-b01)

[✓] IntelliJ IDEA Community Edition (version 2018.3.3)
    • IntelliJ at /Applications/IntelliJ IDEA CE.app
    • Flutter plugin version 31.3.4
    • Dart plugin version 183.5153.38

[✓] Connected device (1 available)
    • Android SDK built for x86 • emulator-5554 • android-x86 • Android 9 (API
      28) (emulator)

【问题讨论】:

【参考方案1】:

已经很晚了,但我还是会回复其他人以提供帮助。

这里的问题不在于处理,而在于您从 Firebase 控制台发送的消息类型。 onBackgroundMessagecallBack 仅在消息是数据消息或带有数据负载的通知消息时调用。

并确保您的消息也添加了click_action: 'FLUTTER_NOTIFICATION_CLICK'

从 Firebase 控制台生成消息也存在一些问题,因此请尝试直接从您的服务器生成消息。 更多详情请查看thread。


【讨论】:

我尝试通过 firebase 控制台发送通知,但不是测试消息,而是包含键值数据的完整消息,但也无法正常工作...看来这不会触发我的后台处理程序 尝试使用设备 fcm 令牌向特定设备发送消息。 是的,谢谢,我在发送仅包含数据的通知时收到了, 太好了,它解决了你的问题,别忘了给答案点赞,这样其他人可以轻松找到解决方案。 成功了。错过了点击动作。非常感谢【参考方案2】:

在我的例子中,FCM onBackgroundMessage 仅在有效负载上没有通知时触发。如果负载上有通知,它会发送什么通知,会在系统托盘上显示通知。

但是,如果您想对发送的数据进行一些编译,请删除有效负载上的通知并仅发送数据


  "to" : "Place the token of the test device here",
  "click_action": "FLUTTER_NOTIFICATION_CLICK",
  "priority": "high",
  "data" : 
     "type" : "anything",
     "title": "Title of Your Notification in Title",
     "key_1" : "Value for key_2"
  


2021 年更新

确保在通知中添加content_available,这对我现在有效。我正在使用firebase_messaging prereplase。在此版本中,他们启用了开箱即用的后台处理程序。https://pub.dev/packages/firebase_messaging/versions/8.0.0-dev.14

  
  "data":  
    "title":"mytitle",
    "body":"mybody",
    "url":"myurl"
  ,
 "notification":  
    "title":"mytitle",
    "body":"mybody",
    "content_available": true
  ,
  "to":"/topics/topic"
  "click_action": "FLUTTER_NOTIFICATION_CLICK",

【讨论】:

这确实有效。但是当应用程序完全终止时,fcm 不支持数据。 在您的场景中,您需要通知和数据。通知由 onBackgroundMessage 处理程序处理,数据由 onMessage 处理。指定的高优先级也很重要。 我从 Java 服务器发送通知。就我而言,从消息中删除通知解决了这个问题。谢谢【参考方案3】:

请确保您在发送通知时添加了 click_action 属性。 确保其值与您在 android 意图中提供的值匹配。


notification: 
    title: 'Title',
    body: 'Body',
    click_action: 'FLUTTER_NOTIFICATION_CLICK'
 

【讨论】:

【参考方案4】:

在 myBackgroundMessageHandler 中,您需要调用该函数将通知保存到本地存储。

例如

Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) async 
  print("onBackgroundMessage: $message");
  //_showBigPictureNotification(message);
    NotificationHandler()._saveNotificationToLocal(message);
  return Future<void>.value();

希望对您有所帮助。

【讨论】:

感谢您的回复,但在应用程序处于后台时它仍然无法正常工作,并且除非单击通知,否则不会保存数据。此外,该函数不打印句子 这就是我的结构。 Future _myBackgroundMessageHandler( Map message) async if (message.containsKey('data')) // 处理数据消息 final dynamic data = message['data'];打印(数据); myAppState().processFCMessageEvent(数据); 如果您打算使用后台消息,还必须确保您没有发送通知 这对我有很大帮助看看这个链接github.com/FirebaseExtended/flutterfire/issues/199 并寻找mobileink 写的评论

以上是关于[Firebase 消息传递]:应用程序在后台时不调用后台消息处理程序方法?的主要内容,如果未能解决你的问题,请参考以下文章

用于 Flutter 的 Firebase 云消息传递 - 可选择处理后台消息错误

应用程序在后台时不显示推送通知

Firebase onMessageReceived(RemoteMessage remoteMessage),当应用程序在后台时不被调用[重复]

有没有办法拦截和禁用默认的后台 FCM 通知并在 firebase 消息传递服务工作者中显示自定义通知

Firebase 云消息传递前台通知在 Vue 中不起作用

Flutter Firebase 消息传递 - 应用打开时未显示推送通知