簡體   English   中英

應用程序被殺死時無法處理 FCM 消息

[英]FCM messages unable to be handled when app is killed

我一直在閱讀各種教程、其他 SO 線程以及官方 Android 開發人員和 Firebase 文檔,但無濟於事。 我已經嘗試了幾乎所有方法,但由於我正在修復以前有效但不再有效的通知系統,因此我的動力和時間都用完了。

我正在使用 Azure 通知中心在其他推送通知平台中向 FCM 分發通知。 我的 FCM 項目僅針對 Android。 我的應用程序使用所有依賴項的最新 NuGet 包版本(Xamarin.Forms 5.xxx、Firebase.Messaging 122.0 等)在 Xamarin.Forms 中構建。

目前,應用程序運行或后台接收的遠程消息通過繼承和實現 FirebaseMessagingService 的自定義服務完美地工作。 一旦應用程序被終止(任務切換器 -> 滑動應用程序),在發送更多消息后,我開始看到包含以下內容的 Logcat 消息:

廣播意圖回調:result=CANCELLED forIntent { act=com.google.android.c2dm.intent.RECEIVE pkg=(有額外的)}

我了解 Google 更改了隱式接收器在 API 26 及更高版本中的工作方式,我的理解是此操作( com.google.android.c2dm.intent.RECEIVE )未包含在異常列表中,因此我不知道如何消息可以在后台監聽和處理。 我最近在 2019 年 7 月在其他線程中讀到,在應用程序被終止時收到的 FCM 消息應該直接發送到通知托盤。 這不是一個普遍的問題,因為許多應用程序在被殺死時發送通知,所以我希望獲得一些當前信息來指導我找到解決方案。

這個意圖廣播是因為隱式接收器更改而被取消,還是我做錯了什么?

我正在使用 Android 10 的 OnePlus 7 Pro 進行測試,所以我想知道這是否是其他人在華為和小米等 OEM 的設備上提到的電池優化問題。

我的應用程序的目標是 Android API 級別 29,最小 API 21

我為我的主要活動和接收器啟用了直接啟動感知,以確保接收器在啟動時和用戶打開應用程序之前攔截我的應用程序的意圖:

<receiver android:directBootAware="true" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND" android:name=".NotificationReceiver">
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <category android:name="<package>" />
    </intent-filter>
</receiver>

我的主要活動包括作為啟動活動的意圖過濾器:

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

我請求以下權限:

  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.CAMERA" />
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
  <uses-permission android:name="android.permission.WAKE_LOCK" />
  <uses-permission android:name="android.permission.GET_ACCOUNTS" />

我在清單<application>標簽中定義了以下meta-data標簽:

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

這些值非常適合在后台接收到的通知,所以我知道它們配置正確。

編輯1:

自發布以來,我做了更多的挖掘和調試,我確實看到了這一點,沒有我的自定義 BroadcastReceiver 也監聽我的應用程序的 c2dm.intent.RECEIVE 操作:

  • 實際上是通過 Google 提供的內部 FirebaseInstanceIdReceiver 終止應用程序時發送的遠程消息,該內部 FirebaseInstanceIdReceiver 是記錄的 FCM 設置的一部分(我刪除了之前提到的 NotificationReceiver 服務)
  • 正在開始我的 FirebaseMessagingService 的自定義實現(見下面的截圖)
  • 沒有在我的 FirebaseMessagingService 中觸發 OnMessageReceived 過載(見下面的截圖)

當應用程序被殺死時收到 FCM 遠程消息時的 logcat

*******FirebaseService 部分代碼:

    [Service(DirectBootAware = true, Exported = true, Enabled = true)]
    [IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
    [IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
    public class MyFirebaseService : FirebaseMessagingService
    {
        public MyFirebaseService()
        {
            
        }

        public override ComponentName StartService(Intent service)
        {
            Log.Info("GCM", $"MyFirebaseService started from intent {service}");

            return base.StartService(service);
        }

        public override void OnMessageReceived(RemoteMessage message)
        {
            var notification = message.GetNotification();
            Log.Info("GCM", $"Received remote message from FCM. Has notification: {notification != null}, has data: {message.Data != null}");

            if (notification != null)
            {
                message.Data.TryGetValue("route", out string route);
                SendNotification(notification.Title, notification.Body, route);
            }
            else
            {
                ParseDataNotification(message);
            }
        }

        ...

我能夠解決這個問題。 我的 FirebaseMessagingService 實現在構造函數中有一個依賴注入調用,當 FirebaseIidInstanceReceiver 在后台啟動服務時失敗。 這導致服務無法啟動,並且在應用程序被終止時未生成 Android 通知。

由於我已經進行了大量挖掘,並且關於該主題的信息非常零散和過時,因此我將嘗試編譯我所知道的結果,在這里找到一個可行的解決方案:

按照此處的步驟操作,特別是設置您的 FCM 項目並下載google-services.json文件。

確保您的清單聲明以下權限:

<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" />

在您的 AndroidManifest <application>標簽中添加以下內容以監聽消息接收:

<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" android:exported="false" />
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
        <category android:name="${applicationId}" />
    </intent-filter>
</receiver>

可選地定義通知通道、通知圖標(必須為白色,允許透明)和通知托盤展開時的通知圖標顏色的默認值,也在<application>清單標簽內:

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

創建一個繼承自FirebaseMessagingService的自定義類。 在 Xamarin.Forms 中,您將需要此類的 Xamarin.Firebase.Messaging NuGet 包。 在您的實現中,您應該覆蓋OnMessageReceived(RemoteMessage)並添加您的應用程序邏輯,該邏輯將處理OnMessageReceived(RemoteMessage)包含notification屬性的消息以及在前台和后台中僅包含data屬性的消息。 你的類應該用以下屬性裝飾(注意 DirectBootAware 是可選的;見下文):

[Service(DirectBootAware = true, Exported = true, Enabled = true)]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
[IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]

如果您希望確保在設備重啟后和設備解鎖之前可以收到通知,您可以考慮讓您的應用程序和 FirebaseMessagingService 實現直接啟動感知(更多信息請點擊此處

在您的 MainActivity 中,確保為運行 Android O 或更高版本的設備創建通知通道,並在OnCreate期間的某個時刻調用此方法:

private void CreateNotificationChannel()
{
    if (Build.VERSION.SdkInt < BuildVersionCodes.O)
    {
        // Notification channels are new in API 26 (and not a part of the
        // support library). There is no need to create a notification
        // channel on older versions of Android.
        return;
    }

    var channelId = GetString(Resource.String./*res id here*/);
    var notificationManager = (NotificationManager)GetSystemService(NotificationService);

    // Don't re-create the notification channel if we already created it
    if (notificationManager.GetNotificationChannel(channelId) == null)
    {
        var channel = new NotificationChannel(channelId,
            "<display name>",
            NotificationImportance.Default);

        notificationManager.CreateNotificationChannel(channel); 
    }
}

將 ProGuard 配置文件 ("proguard.cfg") 添加到您的 Android 項目,以防止 SDK 鏈接器終止 Google Play 和 Firebase 庫。 在 Visual Studio 中編輯此文件的屬性並將 Build Action 設置為ProguardConfiguration 即使下拉列表中缺少該選項,Xamarin 也會識別它。 如果您在構建中使用 d8 和 r8 而不是 dx 和 ProGuard,Xamarin 仍將使用此配置文件並符合您在其中定義的規則。

# Keep commands are required to prevent the linker from killing dependencies not directly referenced in code
# See: https://forums.xamarin.com/discussion/95107/firebaseinstanceidreceiver-classnotfoundexception-when-receiving-notifications

-dontwarn com.google.android.gms.**
-keep class com.google.android.gms.** { *; }
-keep class com.google.firebase.** { *; } 

希望這會有所幫助,如果我錯過了什么,我會更新更多細節。

@AndrewH 解決方案對我有用。 缺少一個細節。 當應用程序被Firebase messaging service也會被調用。 當應用程序被殺死時,只有服務的構造函數會被調用,因為內部 Firebase 知道您的應用程序已被殺死。 因此,在初始化將處理前台通知的任何代碼或與Xamarin forms交互的任何代碼之前,您應該檢查Xamarin forms是否已初始化。 例如:

if (Xamarin.Forms.Forms.IsInitialized)
            {
                // Do stuffs.
            }

否則你的應用程序會在收到來自終止狀態的推送通知時崩潰。

此外,您應該知道,如果應用程序被Xamarin.Forms.Forms.IsInitialized == falseXamarin.Forms.Forms.IsInitialized == false ,則不應嘗試執行任何代碼。 就別管了。 Firebase 只會為您顯示通知。 當用戶在MainActivity.OnCreate()單擊系統托盤中的通知時,您將只處理通知。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM