Firebase(FCM):在通知点击时打开活动并传递数据。安卓

64
应该清晰地实现如何使用Firebase通知和数据。我阅读了很多答案,但似乎无法使其正常工作。以下是我的步骤:
1.) 我正在通过PHP将通知和数据传递给Android,看起来一切正常:
$msg = array
    (
         "body" => $body,
         "title" => $title,
         "sound" => "mySound"
    );

    $data = array
    (

         "user_id" => $res_id,
         "date" => $date,
         "hal_id" => $hal_id,
         "M_view" => $M_view
    );

    $fields = array
    (
        'registration_ids' => $registrationIds,
        'notification' => $msg,
        'data' => $data
    );



    $headers = array
    (
        'Authorization: key='.API_ACCESS_KEY,
        'Content-Type: application/json'
    );

    $ch = curl_init();
    curl_setopt( $ch,CURLOPT_URL, 'https://android.googleapis.com/gcm/send' );
    curl_setopt( $ch,CURLOPT_POST, true );
    curl_setopt( $ch,CURLOPT_HTTPHEADER, $headers );
    curl_setopt( $ch,CURLOPT_RETURNTRANSFER, true );
    curl_setopt( $ch,CURLOPT_SSL_VERIFYPEER, false );
    curl_setopt( $ch,CURLOPT_POSTFIELDS, json_encode( $fields ) );
    $result = curl_exec($ch );
    curl_close( $ch );

2.) 当 Android 接收到通知和数据时,它会显示通知。当我点击这个通知时,它会打开应用程序。但是我不知道如何在应用程序打开时处理数据。当应用程序在前台或后台运行时有一些差异。我现在拥有的代码如下:

public class MyFirebaseMessagingService extends FirebaseMessagingService {

private static final String TAG = "MyFirebaseMsgService";

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {

    String user_id = "0";
    String date = "0";
    String cal_id = "0";
    String M_view = "0";

    if (remoteMessage.getData().size() > 0) {
        Log.d(TAG, "Message data payload: " + remoteMessage.getData());
        user_id = remoteMessage.getData().get("user_id");
        date = remoteMessage.getData().get("date");
        hal_id = remoteMessage.getData().get("hal_id");
        M_view = remoteMessage.getData().get("M_view");
    }

    //Calling method to generate notification
    sendNotification(remoteMessage.getNotification().getBody(), user_id, date, hal_id, M_view);
}

private void sendNotification(String messageBody, String user_id, String date, String hal_id, String M_view) {
    Intent intent = new Intent(this, MainActivity.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);

    intent.putExtra("fcm_notification", "Y");
    intent.putExtra("user_id", user_id);
    intent.putExtra("date", date);
    intent.putExtra("hal_id", hal_id);
    intent.putExtra("M_view", M_view);
    int uniqueInt = (int) (System.currentTimeMillis() & 0xff);
    PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), uniqueInt, intent,
            PendingIntent.FLAG_UPDATE_CURRENT);

    Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
    notificationBuilder.setSmallIcon(R.drawable.ic_launcher)
            .setContentText(messageBody)
            .setAutoCancel(true)
            .setSound(defaultSoundUri)
            .setContentIntent(pendingIntent);

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

3.) 当我使用上述代码并点击通知时,它只会在后台打开应用程序。如果应用程序在前台,则在通知点击时仅会关闭通知。但是,我希望在两种情况下(后台和前台)都能接收数据并打开特定的Activity。我在MainActivity中有以下代码,但我无法获取数据。fcm_notification、date、hal_id返回null。

public class MainActivity extends Activity {
 UserFunctions userFunctions;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    setIntent(intent);
    Intent intent_o = getIntent();
}

@Override
protected void onResume() {
    super.onResume();
    userFunctions = new UserFunctions();
    if(userFunctions.isUserLoggedIn(getApplicationContext())){
        Intent intent_o = getIntent();
        String fcm_notification = intent_o.getStringExtra("fcm_notification") ;
        String user_id = intent_o.getStringExtra("user_id");
        String date = intent_o.getStringExtra("date");
        String hal_id = intent_o.getStringExtra("hal_id");
        String M_view = intent_o.getStringExtra("M_view");
        Intent intent = new Intent(this, JobList.class);

        // THIS RETURNS NULL, user_id = null
        System.out.print("FCM" + user_id);
        startActivity(intent);
        finish();
    }else{
        // user is not logged in show login screen
        Intent login = new Intent(this, LoginActivity.class);
        startActivity(login);
        // Closing dashboard screen
        finish();
    }
}}

如果有人能指导或建议我如何从Firebase在MainActivity.java中检索数据,无论是在前台还是后台,那将是很棒的。

6个回答

79

首先,我会在处理消息文档中提到的详细信息中加入说明。

在“Both”行下的总结中,显示当应用程序处于前台时,负载将在onMessageReceived()中处理。

如果要从onMessageReceived()打开活动,则应检查是否在有效载荷中包含所需的数据,如果有,请调用特定的活动,然后通过意图传递您需要的所有其他详细信息。

现在,如果应用程序处于后台,则文档中提到通知将由Android系统托盘接收,并且可以从意图附加项的额外信息中检索data有效载荷。

仅添加来自我的答案这里的详细信息,它几乎只给出了文档语句和样例链接:

在后台应用程序中处理通知消息

当您的应用程序处于后台时,Android将通知消息发送到系统托盘。默认情况下,用户点击通知会打开应用程序启动器。

这包括包含通知和数据有效载荷的所有消息(以及从通知控制台发送的所有消息)。在这些情况下,通知将传递到设备的系统托盘中,并且数据有效载荷将传递到启动器Activity的意图附加项中。

我认为@ArthurThompson的答案解释得非常清楚:

当您发送带有数据有效载荷(通知和数据)的通知消息并且应用程序处于后台时,您可以从因用户点击通知而启动的意图的额外信息中检索数据。

FCM示例中,当点击通知时启动MainActivity:

if (getIntent().getExtras() != null) {
    for (String key : getIntent().getExtras().keySet()) {
        String value = getIntent().getExtras().getString(key);
        Log.d(TAG, "Key: " + key + " Value: " + value);
    }
}

4
我只能获取到Extras中的有效负载信息。你有什么想法可以在应用程序甚至不在后台时,在启动活动中获取通知文本和标题? - Syeda Zunaira
@SyedaZunaira 一个选择是将 titlebody 作为 data 属性发送,这样你就可以将它们作为附加信息读取。 - Awi
非常感谢!它在我这里可行!并且非常有帮助! - Haya Akkad
非常感谢您的澄清。 - sohel yadav

37

在尝试了所有答案和博客后,找到了解决方案。如果有人需要,请使用此视频作为参考:

https://www.youtube.com/watch?v=hi8IPLNq59o

除了视频之外,在MyFirebaseMessagingService中添加意图(intents)的方法如下:

public class MyFirebaseMessagingService extends FirebaseMessagingService {

private static final String TAG = "MyFirebaseMsgService";

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {

    String user_id = "0";
    String date = "0";
    String hal_id = "0";
    String M_view = "0";

    if (remoteMessage.getData().size() > 0) {
        Log.d(TAG, "Message data payload: " + remoteMessage.getData());
        user_id = remoteMessage.getData().get("user_id");
        date = remoteMessage.getData().get("date");
        cal_id = remoteMessage.getData().get("hal_id");
        M_view = remoteMessage.getData().get("M_view");
    }

    String click_action = remoteMessage.getNotification().getClickAction();

    //Calling method to generate notification
    sendNotification(remoteMessage.getNotification().getBody(), remoteMessage.getNotification().getTitle(), user_id, date, hal_id, M_view, click_action);
}

private void sendNotification(String messageBody, String messageTitle, String user_id, String date, String hal_id, String M_view, String click_action) {
    Intent intent = new Intent(click_action);
    intent.putExtra("user_id", user_id);
    intent.putExtra("date", date);
    intent.putExtra("hal_id", hal_id);
    intent.putExtra("M_view", M_view);

    PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent,
            PendingIntent.FLAG_ONE_SHOT);

    Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
    notificationBuilder.setSmallIcon(R.drawable.ic_launcher)
            .setContentTitle(messageTitle)
            .setContentText(messageBody)
            .setAutoCancel(true)
            .setSound(defaultSoundUri)
            .setContentIntent(pendingIntent);

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

在新的NotificationReceive活动中,在onCreate或onResume中添加以下内容:

    notification_Y_N = (TextView) findViewById(R.id.notification_Y_N);
    user_id_text = (TextView) findViewById(R.id.user_id_text);

    Intent intent_o = getIntent();
    String user_id = intent_o.getStringExtra("user_id");
    String date = intent_o.getStringExtra("date");
    String hal_id = intent_o.getStringExtra("hal_id");
    String M_view = intent_o.getStringExtra("M_view");

    notification_Y_N.setText(date);
    user_id_text.setText(hal_id);

我没有注意到你已经添加了一个答案。晚了8分钟。哈哈 - AL.
不用谢。请接受您的答案,以便您的帖子能够被正确标记。干杯! - AL.
不确定。你应该向视频制作者确认,因为我只是使用这个链接作为 YouTube 视频的示例。 - Vaidas
6
你好!你的代码在前台运行正常,但当应用程序在后台时如何直接指定活动(activity)? - Eggy
当通知未被点击但您想传递某些内容时,请在发送通知时在服务器端使用数据类型而不是通知。更多信息请参见:https://dev59.com/JVoU5IYBdhLWcg3wM08H?noredirect=1&lq=1 - Vaidas
显示剩余2条评论

4
要调用onMessageReceived()方法,您需要使用另一种方法来发送通知(例如创建Web API来发送通知)。然后使用它, 从您的FCM消息中删除通知负载,以便将数据负载传递给onMessageReceived()方法。 当您的应用程序在后台运行时,只有在没有通知负载时,数据负载才会传递到onMessageReceived方法。
如果两个负载都存在,则系统自动处理通知部分(系统托盘),并且您的应用程序在启动器Activity的意图的extras中获取数据负载(在用户点击通知后)。
有关更多信息,请参见以下链接:
- 为什么会发生这种情况?如何处理推送通知? - kws的原始答案。请为他点赞。

2
你不需要自己实现sendNotificationonMessageReceived
发送时:
$data = array
(
    "user_id" => $res_id
    //whatever fields you want to include
);

$msg = array
(
     "body" => $body,
     "title" => $title,
     "data" => $data
     // more fields
);

在你的MainActivity中的安卓端:

private void handleIntent(Intent intent) {
    String user_id= intent.getStringExtra("user_id");
    if(user_id!= null)
        Log.d(TAG, user_id);
}

当然,还有:
@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    handleIntent(intent);
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    handleIntent(getIntent());
}

无论您在data中放置了哪些字段,都将发送到您的意图extra


能否解释一下 onCreate 中的 handleIntent 何时被调用?是在用户点击 FCM 消息时吗? - famfamfam
@famfamfam 请看最后的代码块,如果应用程序关闭并且用户点击消息,则调用 onCreate,如果应用程序打开并且用户点击消息,则调用 onNewIntent - 因此只需实现两者并从中调用 handleIntent - Ohad Cohen

1
首先,如果响应中有数据对象和通知对象,则请请求后端开发人员删除通知对象。 我希望我的类能够提供帮助。
public class MyFirebaseService extends FirebaseMessagingService {
private static final String TAG = "MyFirebaseService";

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
    super.onMessageReceived(remoteMessage);
   

    // Check if message contains a data payload.
    if (remoteMessage.getData().size() > 0) {
        Log.d(TAG, "Message data payload: " + remoteMessage.getData());
        Log.d(TAG, "Message data payload:id " + remoteMessage.getData().get("mode_id"));

      
        sendNotification(remoteMessage.getData().get("body"),
                remoteMessage.getData().get("mode_id"), remoteMessage.getData().get("click_action"));
        
    }
}

private void sendNotification(String messageBody, String id, String clickAction) {
    Intent intent = new Intent(clickAction);
    TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
    stackBuilder.addNextIntentWithParentStack(intent);
    intent.putExtra("id", id);
    intent.putExtra("body", messageBody);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
            | Intent.FLAG_ACTIVITY_CLEAR_TASK);

    PendingIntent pendingIntent =
            stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, "111")
            .setSmallIcon(R.drawable.venus_logo)
            .setContentText(messageBody)
            .setAutoCancel(true)
            .setVibrate(new long[]{1000, 1000, 1000, 1000, 1000})
            .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
            .setContentIntent(pendingIntent)
            .setLights(Color.GREEN, 3000, 3000);

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
        NotificationManager notificationManager =
                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        int importance = NotificationManager.IMPORTANCE_HIGH;
        NotificationChannel notificationChannel = new NotificationChannel("111", "NOTIFICATION_CHANNEL_NAME", importance);
        notificationChannel.enableLights(true);
        notificationChannel.setLightColor(Color.RED);
        notificationChannel.enableVibration(true);
        notificationChannel.setShowBadge(false);
        notificationChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
        assert notificationManager != null;
        notificationBuilder.setChannelId("111");
        notificationManager.createNotificationChannel(notificationChannel);
        notificationManager.notify(0, notificationBuilder.build());
    } else {
        NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
        notificationManager.notify(0, notificationBuilder.build());
    }
  }
}

然后将此添加到您的清单文件中。
    <service
        android:name=".data.services.MyFirebaseService"
        android:exported="false">
        <intent-filter>
            <action android:name="com.google.firebase.MESSAGING_EVENT" />
        </intent-filter>
    </service>
    <activity
        android:name=".ui.notifications.NotificationsDetailsActivity"
        android:excludeFromRecents="true"
        android:launchMode="singleTask"
        android:parentActivityName=".ui.home.HomeActivity"
        android:taskAffinity="">
        <intent-filter>
            <action android:name="co.example.yourApp.ui.notifications_TARGET_NOTIFICATION" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </activity>

0
Firebase文档有一个很棒的表格来解释它的工作原理。

enter image description here

https://firebase.google.com/docs/cloud-messaging/android/receive#handling_messages

所以,如果您在应用程序处于前台时收到了数据和通知,则应在扩展FirebaseMessagingService的服务中(在onMessageReceived方法中)自己创建一个通知。

在其他情况下(应用程序在后台),您可以从Activityintent.extras中获取数据,系统将创建一条通知以打开应用程序的主要活动。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接