Xamarin Forms:在前台模式下读取通知数据时出现问题

问题描述 投票:0回答:1

下面是我的MainActivity代码:

using System;
using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.OS;
using static Firebase.Messaging.RemoteMessage;
using Android.Gms.Common;
using Android.Nfc;
using Android.Util;
using Android.Content;
using Firebase.Messaging;
using NoticationDemo.Droid;
using System.Collections.Generic;
using Xamarin.Forms;
using AndroidX.Core.App;

namespace NoticationDemo.Droid
{
    [Activity(
        Label = "NoticationDemo", 
        Icon = "@mipmap/icon", 
        Theme = "@style/MainTheme", 
        MainLauncher = true,
        LaunchMode = LaunchMode.SingleTop,
        ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize )]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        static readonly string TAG = "MainActivity";
        public bool isNotification = false;
        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
            isNotification = false;
            IsPlayServicesAvailable();
            //background mode or killed mode
            CreateNotificationFromIntent(Intent);

            if (!isNotification)
            {
                LoadApplication(new App("No Notification"));
            }
        }

        public bool IsPlayServicesAvailable()
        {
            int resultcode = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(this);
            if (resultcode != ConnectionResult.Success)
            {
                if (GoogleApiAvailability.Instance.IsUserResolvableError(resultcode))
                {
                    Console.WriteLine($"Error:{GoogleApiAvailability.Instance.GetErrorString(resultcode)}");
                }
                else
                {
                    Console.WriteLine("Error: play services not supported!");
                }
                return false;
            }
            else
            {
                Console.WriteLine("Play services available");
                return true;
            }
        }

        protected override void OnNewIntent(Intent intent)
        {
            //foreground mode
            CreateNotificationFromIntent(intent);
        }

        void CreateNotificationFromIntent(Intent intent)
        {
            if (intent.Extras != null)
            {
                foreach (var key in intent.Extras.KeySet())
                {
                    var notificationName = intent.Extras.GetString("NotificationName");
                    var NotificatioKey = intent.Extras.GetString("NotificationKey");

                    Console.WriteLine("NotificationName:>>" + notificationName);
                    Console.WriteLine("NotificatioKey:>>" + NotificatioKey);

                    if (NotificatioKey == "Superman")
                    {
                        if (notificationName?.Length > 0)
                        {
                            isNotification = true;
                            LoadApplication(new App(notificationName));
                        }
                    }
                }
            }
        }

        public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
        {
            Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);

            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }
}

[Service(Enabled = true, Exported = true)]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class FirebaseNotificationService : FirebaseMessagingService
{
    public override void OnNewToken(string token)
    {
        base.OnNewToken(token);
        Console.WriteLine($"Token received:>> {token}");
        SendRegistrationTokenToMainPRoject(token);
    }

    public override void OnMessageReceived(RemoteMessage message)
    {
        base.OnMessageReceived(message);
        try
        {
            string body = System.Net.WebUtility.UrlDecode(message.GetNotification().Body.ToString()).Replace("'", "'");
            string header = System.Net.WebUtility.UrlDecode(message.GetNotification().Title.ToString()).Replace("'", "'");
            SendNotificatios(body, header, message.Data);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error:>>" + ex);
        }
    }

    public void SendNotificatios(string body, string Header, IDictionary<string, string> data)
    {
        var intent = new Intent(this, typeof(MainActivity));
        intent.AddFlags(ActivityFlags.ClearTop);
        foreach (var key in data.Keys)
        {
            intent.PutExtra(key, data[key]);
        }
        var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.Mutable);

        if (Build.VERSION.SdkInt < BuildVersionCodes.O)
        {
            var notificationBuilder = new NotificationCompat.Builder(this)
                        .SetContentTitle(Header)
                        .SetContentText(body)
                        .SetSmallIcon(Resource.Mipmap.icon)
                        .SetAutoCancel(true)
                        .SetContentIntent(pendingIntent);

            var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;

            notificationManager.Notify(new Random().Next(), notificationBuilder.Build());
        }
        else
        {
            var notificationBuilder = new NotificationCompat.Builder(this, Utils.CHANNEL_ID)
                        .SetContentTitle(Header)
                        .SetContentText(body)
                        .SetSmallIcon(Resource.Mipmap.icon)
                        .SetAutoCancel(true)
                        .SetContentIntent(pendingIntent);

            var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;

            NotificationChannel channel = new NotificationChannel(Utils.CHANNEL_ID, "FCM Notifications", NotificationImportance.Default);
            notificationManager.CreateNotificationChannel(channel);

            notificationManager.Notify(new Random().Next(), notificationBuilder.Build());
        }
    }

    void SendRegistrationTokenToMainPRoject(string token)
    {
        try
        {
            //Send Refresh to you FCM Server Here
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

AndroidManifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.noticationdemo">
    <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
    <application android:label="NoticationDemo.Android" android:theme="@style/MainTheme">
      <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>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>

Utils.cs

public class Utils
{
    public static readonly string CHANNEL_ID = "CB_FCM_CHANNEL";
    public static readonly int NOTIFICATION_ID = 100;
}

通知负载:

{
"to" : "device token",
"notification" : {
    "body" : "Hi1",
    "title": "Notification1"
},
"data" : {
    "NotificationName": "notification1",
     "NotificationKey" : "test"
    }
}

我正在从通知中读取NotificationName,并将其作为参数传递给App.xaml.cs,并将其显示在主页的UI 上。

public App(string notificationName)
{
    InitializeComponent();
    MainPage = new MainPage(notificationName);
}

public partial class MainPage : ContentPage
{
    public MainPage(string notificationName)
    {
        InitializeComponent();
        notificationname_label.Text = notificationName; 
    }
}

我的问题是,对于在前台模式下接收的所有通知,始终显示前台模式下的第一个通知。

例如:我在前台模式下收到了Notification1。如果我点击它,通知 1 数据将被读取并显示在 UI 上。同样,如果我收到新通知“通知 2”,如果我点击该通知,则通知 1 数据将再次显示在 UI 上。始终在前台模式下读取第一个通知数据。在后台模式下没有这样的问题。

这是我的 demopostman Collection 来重现此问题。

如何重现此问题:

  • 在 Android 设备中运行演示项目并获取您的设备 FCM 令牌。将其添加到邮递员收集 API 的
    your device token
    部分。
  • 然后在前台模式下发送名为Notification 1的API。当点击它时,通知 1 文本将显示在 UI 上。
  • 然后,如果您在前台模式下发送通知 2 并再次点击它,UI 上将显示 notification1 文本,而不是 notification2。
  • 显示在前台模式下收到的所有通知的初始通知数据。但在后台模式下没有这样的问题,并且在后台模式下一切正常。

参考博客:

  1. https://learn.microsoft.com/en-us/xamarin/android/data-cloud/google-messaging/remote-notifications-with-fcm?tabs=windows
  2. https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/local-notifications#handle-incoming-notifications-on-android

更新

我在

MessagingCenter.Send
内添加了
CreateNotificationFromIntent
,如下所示:

void CreateNotificationFromIntent(Intent intent)
{
    if (intent.Extras != null)
    {
        foreach (var key in intent.Extras.KeySet())
        {
            var notificationName = intent.Extras.GetString("NotificationName");
            var NotificationKey = intent.Extras.GetString("NotificationKey");

            Console.WriteLine("NotificationName:>>" + notificationName);
            Console.WriteLine("NotificationKey:>>" + NotificationKey);

            if (NotificationKey == "test")
            {
                if (notificationName?.Length > 0)
                {
                    isNotification = true;
                    MessagingCenter.Send<Xamarin.Forms.Application, string>(Xamarin.Forms.Application.Current, "NotificationName", notificationName);
                    LoadApplication(new App(notificationName));
                }
            }
        }
    }
}

MainPage
部分更新了
MessagingCenter.Subscribe
内容。

public partial class MainPage : ContentPage
{
    public MainPage(string notificationName)
    {
        InitializeComponent();
        //notificationname_label.Text = notificationName;
        MessagingCenter.Subscribe<Xamarin.Forms.Application, string>(this, "NotificationName", async (sender, arg) =>
        {
            notificationname_label.Text = arg;
        });
    }
}

但是当点击通知时,通知名称不会显示在用户界面上。当我通过添加断点进行调试时,

MessagingCenter.Send
MessagingCenter.Subscribe
无限次调用,但通知名称在主页UI上不可见。

xamarin.forms push-notification foreground
1个回答
0
投票

我的问题是,对于在前台模式下接收的所有通知,始终显示前台模式下的第一个通知。

这是因为您通过

notificationname_label.Text = notificationName;
在 MainPage 的构造函数中设置了 UI 标签。当应用程序在前台运行时,只有在 MainPage 第一次初始化时才会调用 App.cs 和 MainPage 的构造函数。

所以你可以参考关于使用INotificationManager辅助类接收通知的官方文档

此外,您还可以使用 MessagingCenter 来完成此操作。这是一个关于使用MessagingCenter通知UI通知已更新的案例

更新:

如果您使用

MessagingCenter
,您可以在收到新通知名称时发送它。并在 MainPage 的构造函数中订阅它:

发送新的通知名称:

MessagingCenter.Send<Xamarin.Forms.Application, string>(Xamarin.Forms.Application.Current, "NotificationName","the new notification name you want to send")

接收新通知名称:

        public MainPage()
        {
            InitializeComponent();

            // Subscribe to a message (which the ViewModel has also subscribed to) to display an alert
            MessagingCenter.Subscribe<Xamarin.Forms.Application, string>(Xamarin.Forms.Application.Current, "NotificationName", async (sender, arg) =>
            {
                notificationname_label.Text = arg; 
            });
        }

更多信息,您可以参考使用MessagingCenter通知UI通知已更新以及MessagingCenter官方示例

© www.soinside.com 2019 - 2024. All rights reserved.