Android API28下目标页已显示时如何不加FLAG_ACTIVITY_NEW_TASK触发onNewIntent
核心原理说明
API 28(Android 9)开始对后台服务启动 Activity 做了强制限制:从非 Activity 上下文(比如 Service、BroadcastReceiver)启动 Activity 必须携带 FLAG_ACTIVITY_NEW_TASK,否则会抛出异常,这个系统限制无法绕过。下面的方案都是在遵守该限制的前提下,实现不重建 MyScreen 实例、触发 onNewIntent 回调的效果
方案1:配置Activity启动模式+配合Intent Flag(最简便)
仅需修改两处配置,原有业务逻辑完全不用改:
- 在
AndroidManifest.xml中给 MyScreen 设置singleTop启动模式:
<activity android:name=".MyScreen" android:launchMode="singleTop" />
- 在 MyFirebaseMessagingService 中构建 Intent 时,除了系统要求的
FLAG_ACTIVITY_NEW_TASK,额外新增FLAG_ACTIVITY_SINGLE_TOP:
// Java示例 Intent intent = new Intent(this, MyScreen.class); // 系统强制要求必须加的Flag intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 配合singleTop启动模式,栈顶已存在MyScreen时就会回调onNewIntent,不会重建 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); // 写入你需要传递的推送参数 intent.putExtra("push_data", 你的推送参数); startActivity(intent);
// Kotlin示例 val intent = Intent(this, MyScreen::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) putExtra("push_data", 你的推送参数) } startActivity(intent)
配置完成后如果当前栈顶已经是 MyScreen 实例,系统会直接回调 onNewIntent 方法,和你在 API 27 及以下版本的原有处理逻辑完全一致。
方案2:本地广播中转(完全避免直接从Service启动Activity)
如果不想修改 Activity 启动模式,可以用本地广播做中转,只有当 MyScreen 不在前台时才启动新实例:
- 在 MyScreen 中注册本地广播接收器接收推送点击事件:
private val pushReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { intent?.getStringExtra("push_data")?.let { data -> // 直接在当前已存在的MyScreen实例处理业务逻辑 handlePushData(data) } } } override fun onResume() { super.onResume() LocalBroadcastManager.getInstance(this) .registerReceiver(pushReceiver, IntentFilter("ACTION_PUSH_CLICKED")) } override fun onPause() { super.onPause() LocalBroadcastManager.getInstance(this).unregisterReceiver(pushReceiver) }
- 在 MyFirebaseMessagingService 中处理通知点击时先发送广播,广播未被接收(说明 MyScreen 不在前台)再启动 Activity:
val pushBroadcast = Intent("ACTION_PUSH_CLICKED").apply { putExtra("push_data", 你的推送参数) } val isHandled = LocalBroadcastManager.getInstance(this).sendBroadcast(pushBroadcast) if (!isHandled) { // MyScreen不在前台,正常启动新实例即可 val activityIntent = Intent(this, MyScreen::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) putExtra("push_data", 你的推送参数) } startActivity(activityIntent) }
内容的提问来源于stack exchange,提问作者Andrew Kolubov
相关产品推荐
相关产品推荐

