web-dev-qa-db-ja.com

アプリがフォアグラウンドまたはバックグラウンドにある場合にFCMで通知を処理する方法

Firebaseを使用してプロジェクトをビルドしました。
FCM(firebase cloud message)も使用します。
しかし問題がある。
アプリがバックグラウンドにあるとき、FCMを処理できません(カスタム通知を作成します)。

公式サイトのチュートリアル はそれを言った
ケース1:アプリフォアグラウンド-> "onMessageReceived()"をオーバーライドして、カスタム通知を作成します。
ケース2:アプリの背景->システムが通知を直接作成します。私たちは何もする必要がなく、何もできません。この場合、「onMessageReceived()」はトリガーされないためです。

ただし、アプリがバックグラウンドのときに何もできない場合、カスタム通知を作成できません。 (例:ユーザーが通知をクリックすると、ウィンドウがポップアップして詳細情報が表示されます。)

では、アプリがバックグラウンドにあるときにFCMで通知を処理するにはどうすればよいですか?

5
Leon Chang

悪い知らせがあります。
Googleは、バージョン「com.google.firebase:firebase-messaging:11.6.0」のFirebaseソースコードを変更しました。
handelIntentは「パブリックファイナルvoidメソッド」になりました。つまり、それを上書きすることはできません。
ソリューションを使用する場合は、バージョンを「com.google.firebase:firebase-messaging:11.4.2」に変更します



私のやり方を試してください。 Android 6.0上記(APIレベル23))でプロジェクトのビルドバージョンで完全に動作し、すでに試してみました。

公式サイトのチュートリアル より良い方法があります

公式サイトによると、アプリがバックグラウンドで動作している場合、システムによって通知が作成されます。そのため、「onMessageReceived()」をオーバーライドして処理することはできません。 「onMessageReceived()」は、アプリがフォアグラウンドにあるときにのみトリガーされるためです。

しかし、真実はそうではありません。実際には、通知(アプリがバックグラウンドにある場合)はFirebase Libraryによって作成されます。

Firebaseライブラリコードをトレースした後。もっと良い方法を見つけます。

ステップ1. FirebaseMessagingServiceの「onMessageReceived()」の代わりに「handleIntent()」をオーバーライドする
なぜ:
メソッドがトリガーされるため、アプリがフォアグラウンドまたはバックグラウンドのいずれかにあります。したがって、どちらの場合もFCMメッセージを処理し、カスタム通知を作成できます。

@Override
public void handleIntent(Intent intent) {
    Log.d( "FCM", "handleIntent ");
}


ステップ2. FCMからのメッセージを解析する
どうやって:
設定したメッセージの形式がわからない場合。印刷して解析してみてください。
これは基本的なイラストです

Bundle bundle = intent.getExtras();
if (bundle != null) {
    for (String key : bundle.keySet()) {
        Object value = bundle.get(key);
        Log.d("FCM", "Key: " + key + " Value: " + value);
    }
}


ステップ2.アプリがバックグラウンドにあるときにFirebaseライブラリによって作成された通知を削除する
なぜ:
カスタム通知を作成できます。ただし、Firebase Libraryによって作成された通知はそのまま残ります(実際には「 "super.handleIntent(intent)" "によって作成されます。詳細な説明は以下のとおりです。)」。次に、2つの通知があります。それはかなり奇妙です。そのため、Firebase Libraryによって作成された通知を削除する必要があります

方法(プロジェクトのビルドレベルはAndroid 6.0)):
削除する通知を認識して、情報を取得します。そして、「notificationManager.cancel()」を使用してそれらを削除します。

private void removeFirebaseOrigianlNotificaitons() {

    //check notificationManager is available
    NotificationManager notificationManager = 
        (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    if (notificationManager == null )
        return;

    //check api level for getActiveNotifications()
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
        //if your Build version is less than Android 6.0
        //we can remove all notifications instead. 
        //notificationManager.cancelAll();
        return;
    }


    //check there are notifications
    StatusBarNotification[] activeNotifications = 
        notificationManager.getActiveNotifications();
    if (activeNotifications == null)
        return;

    //remove all notification created by library(super.handleIntent(intent))
    for (StatusBarNotification tmp : activeNotifications) {
        Log.d("FCM StatusBarNotification", 
            "StatusBarNotification tag/id: " + tmp.getTag() + " / " + tmp.getId());
        String tag = tmp.getTag();
        int id = tmp.getId();

        //trace the library source code, follow the rule to remove it.
        if (tag != null && tag.contains("FCM-Notification"))
            notificationManager.cancel(tag, id);
    }
}

サンプルコード全体:

public class MyFirebaseMessagingService extends FirebaseMessagingService {

private static int notificationCount=0;

@Override
public void handleIntent(Intent intent) {
    //add a log, and you'll see the method will be triggered all the time (both foreground and background).
    Log.d( "FCM", "handleIntent");

    //if you don't know the format of your FCM message,
    //just print it out, and you'll know how to parse it
    Bundle bundle = intent.getExtras();
    if (bundle != null) {
        for (String key : bundle.keySet()) {
            Object value = bundle.get(key);
            Log.d("FCM", "Key: " + key + " Value: " + value);
        }
    }

    //the background notification is created by super method
    //but you can't remove the super method. 
    //the super method do other things, not just creating the notification
    super.handleIntent(intent);

    //remove the Notificaitons
    removeFirebaseOrigianlNotificaitons();

    if (bundle ==null)
        return;

    //pares the message
    CloudMsg cloudMsg = parseCloudMsg(bundle);

    //if you want take the data to Activity, set it
    Bundle myBundle = new Bundle();
    myBundle.putSerializable(TYPE_FCM_PLATFORM, cloudMsg);
    Intent myIntent = new Intent(this, NotificationActivity.class);
    myIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    myIntent.putExtras(myBundle);
    PendingIntent pendingIntent = PendingIntent.getActivity(this, notificationCount, myIntent, PendingIntent.FLAG_UPDATE_CURRENT);

    //set the Notification
    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
            .setSmallIcon(R.mipmap.icon)
            .setContentTitle(cloudMsg.getTitle())
            .setContentText(cloudMsg.getMessage())
            .setAutoCancel(true)
            .setContentIntent(pendingIntent);

    NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.notify(notificationCount++, notificationBuilder.build());
}



/**
 * parse the message which is from FCM
 * @param bundle
 */
private CloudMsg parseCloudMsg(Bundle bundle) {
    String title = null, msg=null;

    //if the message is sent from Firebase platform, the key will be that
    msg = (String) bundle.get("gcm.notification.body");

    if(bundle.containsKey("gcm.notification.title"))
    title = (String) bundle.get("gcm.notification.title");

    //parse your custom message
    String testValue=null;
    testValue =  (String) bundle.get("testKey");

    //package them into a object(CloudMsg is your own structure), it is easy to send to Activity.
    CloudMsg cloudMsg = new CloudMsg(title, msg, testValue);
    return cloudMsg;
}


/**
 * remove the notification created by "super.handleIntent(intent)"
 */
    private void removeFirebaseOrigianlNotificaitons() {

    //check notificationManager is available
    NotificationManager notificationManager = 
        (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    if (notificationManager == null )
        return;

    //check api level for getActiveNotifications()
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
        //if your Build version is less than Android 6.0
        //we can remove all notifications instead. 
        //notificationManager.cancelAll();
        return;
     }

    //check there are notifications
    StatusBarNotification[] activeNotifications = 
        notificationManager.getActiveNotifications();
    if (activeNotifications == null)
        return;

    //remove all notification created by library(super.handleIntent(intent))
    for (StatusBarNotification tmp : activeNotifications) {
        Log.d("FCM StatusBarNotification", 
            "tag/id: " + tmp.getTag() + " / " + tmp.getId());
        String tag = tmp.getTag();
        int id = tmp.getId();

        //trace the library source code, follow the rule to remove it.
        if (tag != null && tag.contains("FCM-Notification"))
            notificationManager.cancel(tag, id);
    }
}
}
7
Leon Chang

ただし、アプリがバックグラウンドのときに何もできない場合、カスタム通知を作成できません。 (例:ユーザーが通知をクリックすると、ウィンドウがポップアップして詳細情報が表示されます。)

では、アプリがバックグラウンドにあるときにFCMで通知を処理するにはどうすればよいですか?

まず、fcmサーバーに送信する正しいメッセージペイロードを作成する必要があります。例:

_{
  "to": "topic_name",
  "priority": "high",
  "data": {
    "field1": "field1 value" 
    "field2": "field2 value" 
  }

  "notification" : {
      "body" : "Lorem ipsum",
      "title" : "sampke title" 
      "click_action": "SHOW_DETAILS" 
    }
}
_

dataペイロードは、ユーザーが通知をクリックした後にメッセージの詳細として表示する実際のデータです。notificationペイロードは、生成された通知の外観を表します(設定できる属性は他にもあります)。通知を自分で作成する必要があります。ここでプロパティを設定するだけです。

ユーザーが通知をタップした後にアクティビティを表示するには、_click_action_に対応するインテントフィルターを設定する必要があります。

_<intent-filter>
     <action Android:name="SHOW_DETAILS"/>
     <category Android:name="Android.intent.category.DEFAULT"/>
 </intent-filter>
_

そのため、ユーザーが通知をタップすると、インテントフィルターを超えるアクティビティが自動的に起動されます。最後のステップは、通知のタップ後にアクティビティが起動されたときにデータを取得することです。とても簡単です。カスタムデータはバンドルを介してアクティビティに渡されます。アクティビティのonCreateメソッド内で、次のようなことを行います。

_Bundle bundle = getIntent().getExtras();
if(bundle.getString("action").equals("SHOW_DETAILS")) /*This indicates activity is launched from notification, not directly*/
{
 //Data retrieved from notification payload send 
 String filed1 = bundle.getString("field1");
 String filed2 = bundle.getString("field2");
}
_

上記のすべては、アプリが実行されていないか、バックグラウンドで実行されている場合に有効です。アプリがフォアグラウンドの場合、通知は作成されません。代わりに、onMessageReceived()イベントを受け取るので、そこで同じデータを処理できます(方法は知っていると思います)。

参照:

https://firebase.google.com/docs/cloud-messaging/http-server-refhttps://github.com/firebase/quickstart-Android/tree/master/メッセージング

2
user1209216

Androidアプリでカスタム通知を作成するには、FCMデータメッセージを使用する必要があります。アプリがバックグラウンドでも、onMessageReceivedが呼び出されるため、データを処理できますカスタム通知を表示します。

https://firebase.google.com/docs/cloud-messaging/Android/receive

サーバーから送信する必要があるデータメッセージ形式:

{"message":{
"token":"Your Device Token",
"data":{
  "Nick" : "Mario",
  "body" : "great match!",
  "Room" : "PortugalVSDenmark"
}
}
}
2
Rajesh.k

[〜#〜] fcm [〜#〜]アプリが停止された場合、 で説明されているように、バックグラウンド通知を送信しません回答handleIntent()ソリューションについて一部のデバイスや古いバージョンの[〜#〜] fcm [〜#〜]、あなたが@override firebaseの公式ドキュメントに記載されていないメソッドを使用すると、ここで問題が発生する可能性があり、 独自のリスク !で使用します。

解決策は何ですか?

[〜#〜] fcm [〜#〜]の横にTelegramのような独自のPush-notification-serviceを使用する必要があります。

またはSyncAdapterの横に[〜#〜] gcm [〜#〜]を使用しますGmailのように。

したがって、これらのアプリのように正常に動作する必要がある場合は、独自のハックを使用する必要があります。

0
user6490462