web-dev-qa-db-ja.com

Xamarin Androidアプリが停止したときにプッシュ通知を受け取るにはどうすればよいですか?

多くの過去に機能したかもしれないさまざまな種類のソリューションを見ましたが、私自身のために機能した確かなものはありません。そして、それは人々が何を言うか、何がうまくいかないか、何が変わったかなどの地雷原です。しかし、私は解決策だけでなく、うまくいけば理解を探そうとしています。今、私はひどく混乱しています。


今できること-アプリがフォアグラウンド/バックグラウンドにある場合、Xamarin Formsアプリ(Android)はプッシュ通知を受信できます。また、インターセプトすることもできますユーザーがタップしたときにこれらの通知が表示されるので、アプリに何をすべきかを伝えることができます。

私がやろうとしていること-本質的に上記のものですが、アプリが完全に停止した状態です。


Azure Notification Hubに接続されたFirebase Messagingセットアップを持っています-残念ながら、私はAzureから離れません(誰かがそれをドロップすることを提案した場合に備えて)。私が現在持っているもののほとんどは、さまざまなMicrosoftドキュメントからまとめることができた情報です( ここ AppCenterは使用しません-これを使用して、便利なコードを相互参照します herehere 、および here )、その他のStackOverflowの質問( herehere など) ここ -リンクするには多すぎます)およびXamarinフォーラム-繰り返しになりますが、使用されている古いコードがある場合は、お詫び申し上げます(お知らせください-最善を尽くして使用しています)日付メソッドなど)。

送信しているプッシュ通知のタイプはData Messagesここで読みます 、カスタムデータを使用しています私の通知では、以下に示すように、これが送信したいプッシュの正しいタイプであることを理解しています。

_{
    "data": {
        "title": "Title Test",
        "body": "Push notification body test",
        "area": "SelectedPage"
    }
}
_

以下は、これまでにプッシュ通知を処理するためにプロジェクトで設定した現在のコードです。

マニフェスト

_<?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.pushtesting" Android:installLocation="auto">
    <uses-sdk Android:minSdkVersion="21" Android:targetSdkVersion="28" />
    <application Android:label="Push Testing">

        <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>
_

MainActivity.cs

私は_LaunchMode = LaunchMode.SingleTop_を持っています。これは、新しいアクティビティを作成する代わりに現在のアクティビティがまだ使用されていることを確認するためです-たとえば、ユーザーが通知をタップした場合-通知と追加のコードの実装(以下) )これが正しいことを示唆しているようです。

_protected override void OnNewIntent(Intent intent) {
    base.OnNewIntent(intent);

    String area = String.Empty;
    String extraInfo = String.Empty;

    if (intent.Extras != null) {
        foreach (String key in intent.Extras.KeySet()) {
            String value = intent.Extras.GetString(key);
            if (key == "Area" && !String.IsNullOrEmpty(value)) {
                area = value;
            } else if (key == "ExtraInfo" && !String.IsNullOrEmpty(value)) {
                extraInfo = value;
            }
        }
    }
    NavigationExtension.HandlePushNotificationNavigation(area, extraInfo);
}
_

OnNewIntentを使用して、ユーザーがプッシュ通知を操作したときにプッシュ通知をインターセプトします。

MyFirebaseMessaging.cs

_using System;
using System.Threading.Tasks;
using Android.App;
using Android.Content;
using Android.Support.V4.App;
using Android.Util;
using Firebase.Messaging;
using PushTesting.Models;
using WindowsAzure.Messaging;

namespace PushTesting.Droid.Services {

    [Service]
    [IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
    public class OcsFirebaseMessaging : FirebaseMessagingService {

        private const String NotificationChannelId = "1152";
        private const String NotificationChannelName = "Push Notifications";
        private const String NotificationChannelDescription = "Receive notifications";
        private NotificationManager notificationManager;

        public override void OnNewToken(String token) => SendTokenToAzure(token);

        /// <summary>
        /// Sends the token to Azure for registration against the device
        /// </summary>
        private void SendTokenToAzure(String token) {
            try {
                NotificationHub hub = new NotificationHub(Constants.AzureConstants.NotificationHub, Constants.AzureConstants.ListenConnectionString, Android.App.Application.Context);

                Task.Run(() => hub.Register(token, new String[] { }));
            } catch (Exception ex) {
                Log.Error("ERROR", $"Error registering device: {ex.Message}");
            }
        }

        /// <summary>
        /// When the app receives a notification, this method is called
        /// </summary>
        public override void OnMessageReceived(RemoteMessage remoteMessage) {
            Boolean hasTitle = remoteMessage.Data.TryGetValue("title", out String title);
            Boolean hasBody = remoteMessage.Data.TryGetValue("body", out String body);
            Boolean hasArea = remoteMessage.Data.TryGetValue("area", out String area);
            Boolean hasExtraInfo = remoteMessage.Data.TryGetValue("extraInfo", out String extraInfo);

            PushNotificationModel Push = new PushNotificationModel {
                Title = hasTitle ? title : String.Empty,
                Body = hasBody ? body : String.Empty,
                Area = hasArea ? area : String.Empty,
                ExtraInfo = hasExtraInfo ? extraInfo : String.Empty
            };

            SendNotification(Push);
        }

        /// <summary>
        /// Handles the notification to ensure the Notification manager is updated to alert the user
        /// </summary>
        private void SendNotification(PushNotificationModel Push) {
            // Create relevant non-repeatable Id to allow multiple notifications to be displayed in the Notification Manager
            Int32 notificationId = Int32.Parse(DateTime.Now.ToString("MMddHHmmsss"));

            Intent intent = new Intent(this, typeof(MainActivity));
            intent.AddFlags(ActivityFlags.ClearTop | ActivityFlags.SingleTop);
            intent.PutExtra("Area", Push.Area);
            intent.PutExtra("ExtraInfo", Push.ExtraInfo);

            PendingIntent pendingIntent = PendingIntent.GetActivity(this, notificationId, intent, PendingIntentFlags.UpdateCurrent);
            notificationManager = (NotificationManager)GetSystemService(Context.NotificationService);

            // Creates Notification Channel for Android devices running Oreo (8.0.0) or later
            if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O) {
                NotificationChannel notificationChannel = new NotificationChannel(NotificationChannelId, NotificationChannelName, NotificationImportance.High) {
                    Description = NotificationChannelDescription
                };

                notificationManager.CreateNotificationChannel(notificationChannel);
            }

            // Builds notification for Notification Manager
            Notification notification = new NotificationCompat.Builder(this, NotificationChannelId)
            .SetSmallIcon(Resource.Drawable.ic_launcher)
            .SetContentTitle(Push.Title)
            .SetContentText(Push.Body)
            .SetContentIntent(pendingIntent)
            .SetAutoCancel(true)
            .SetShowWhen(false)
            .Build();

            notificationManager.Notify(notificationId, notification);
        }
    }
}
_

次に、最後にOnNewToken()を使用してAzureに登録するFirebaseクラス、OnMessageReceived()がオーバーライドされるので、受信したプッシュ通知を処理してからSendNotification()通知の作成と表示に使用されています。

どんな助けも高く評価されます!

GitHubにサンプルプロジェクトを追加

3
MattVon

私にはいくつかの問題がありました(詳細 ここ ):

  • Androidアプリのアーキテクチャと、FirebaseMessagingServiceのライフサイクルとの関連性:アプリ、サービス、レシーバーがあります。「アプリ」が(ユーザーの観点から)閉じているときは、最近、サービス/レシーバーは「インテント」(受信通知メッセージ)に反応することができます。アプリが「強制停止」されると、UI、サービス、レシーバーは実行されません。
  • Visual Studioは、デバッグセッションの終了後にアプリを強制的に停止します。 「停止した」アプリを診断するには、アプリをデバイスで実行し、デバイスログを確認する必要があります。
  • FirebaseMessagingServiceライフサイクルは、アプリが停止状態になると、アクターが共有アプリのプロパティまたはメソッドにアクセスできなくなる(抽象化を削除し、コードプラットフォーム固有にすることで、特にこのようなDependencyServiceを回避する必要がありました)使用できなかったため使用できません)。
  • MSドキュメントは古くなっています。 Firebaseマニフェストエントリでは、たとえばReceiver要素は不要になりました。
  • Visual Studioの「停止した」アプリにデバッガーをアタッチすることはできません。
  • デバイスのエラーメッセージは不可解です。

結局のところ、解決策は「表示」->「その他のウィンドウ」->「デバイスログ」でアプリを2回実行して、強制停止状態を回避し、タクトライフサイクルの問題を発見することでした。 FirebaseMessagingServiceの共有アプリライブラリ。

1
Frank