添加click_action后Android FCM后台/杀死状态无法接收推送求助
我来帮你捋清楚这个问题,FCM的消息类型差异就是核心原因!
问题根源:FCM两种消息类型的处理逻辑差异
FCM有两种核心消息类型,它们的分发逻辑完全不同:
- Notification消息:当App处于后台/被杀死状态时,系统会直接接管并自动展示通知,不会触发你的
onMessageReceived()方法;只有App在前台时,才会走到onMessageReceived()。 - Data消息:无论App处于什么状态(前台/后台/杀死),都会完整传递到
onMessageReceived(),所有通知展示、跳转逻辑都由你自己控制。
从你提供的Bundle内容来看,当前的消息是混合类型(包含gcm.notification.*前缀的字段),所以后台/杀死状态下系统直接展示通知,但你的自定义逻辑(比如prepareIntent()里的Session判断)根本没机会执行,甚至可能因为系统处理click_action时的状态问题,导致看起来像是没收到通知。
解决方案:改用纯Data类型消息
要彻底解决这个问题,最佳方案是让服务器发送纯Data消息,把所有需要的参数(包括原本Notification里的title、body、click_action,还有你的业务参数userId、flag等)都放到data字段里。这样不管App状态如何,都会走到onMessageReceived(),你可以完全控制通知的展示和跳转。
1. 调整服务器端消息格式
把原本的Notification字段全部移到data中,示例JSON格式如下:
{ "to": "DEVICE_TOKEN", "data": { "title": "Someone try to login with your credentials", "body": "TEST MESSAGE", "click_action": "com.my.push.activities.OPEN_NOTIFICATION_LIST", "notification_id": "1140", "notificationId": "2047669", "userId": "140128", "flag": "0", // 其他业务参数... } }
2. 修改客户端代码
纯Data消息的remoteMessage.getNotification()会返回null,所以你需要从remoteMessage.getData()里获取所有参数:
调整prepareIntent()方法
public Intent prepareIntent(RemoteMessage remoteMessage) { Map<String, String> response = remoteMessage.getData(); Intent intent; if (SessionContext.isLoggedIn()) { String clickAction = response.get("click_action"); // 统一用click_action构建Intent,同时添加默认category确保能找到目标页面 intent = new Intent(clickAction); intent.addCategory(Intent.CATEGORY_DEFAULT); // 传递业务参数到目标页面 intent.putExtra("userId", response.get("userId")); intent.putExtra("flag", response.get("flag")); } else { intent = new Intent(this, LoginActivity.class); } // 添加Flag确保跳转逻辑符合预期(比如清除栈顶、新建任务) intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); return intent; }
调整showNotification()方法
从data中获取通知标题、内容,不再依赖remoteMessage.getNotification():
public void showNotification(RemoteMessage remoteMessage) { try { Map<String, String> response = remoteMessage.getData(); Intent intent = prepareIntent(remoteMessage); PendingIntent pIntent = PendingIntent.getActivity(this, Integer.parseInt(response.get("notificationId")), // 用唯一ID区分不同通知的PendingIntent intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); // 适配Android 12+的Flag NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "D4E_ANDROID") .setContentTitle(response.get("title")) .setContentText(response.get("body")) .setSmallIcon(R.drawable.d4e_logo) .setContentIntent(pIntent) .setAutoCancel(true) .addAction(R.drawable.view_icon, "View", pIntent); NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); notificationManager.notify(Integer.parseInt(response.get("notificationId")), builder.build()); } catch (Exception exception) { Log.e("OnREC", exception.toString()); } }
3. 确保目标Activity配置正确
在AndroidManifest.xml中,给click_action对应的Activity添加intent-filter,保证系统能找到它:
<activity android:name=".activities.NotificationListActivity"> <intent-filter> <action android:name="com.my.push.activities.OPEN_NOTIFICATION_LIST" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
为什么不推荐在Notification消息里用click_action?
虽然FCM允许在Notification消息里设置click_action,但这种方式的控制权在系统手上:
- 当App被杀死时,系统会直接启动对应的Activity,但不会经过你自定义的Session判断逻辑(比如你代码里的
SessionContext.isLoggedIn()),可能导致未登录用户直接进入需要权限的页面。 - 系统展示的通知样式相对固定,无法完全自定义(比如你代码里的自定义Action按钮)。
而纯Data消息完全由你控制,能适配所有场景,逻辑更统一,也更灵活。
内容的提问来源于stack exchange,提问作者Rakesh L

