Android中Service使用putExtra向Activity传值为何始终显示初始值
异常核心成因
本质是PendingIntent的系统级缓存复用机制,导致跨组件传递的Extra数据一直停留在首次创建时的初始值,所有现象完全匹配该机制的触发逻辑:
- PendingIntent的判定规则是:只要两个PendingIntent的requestCode、目标Component、Action、Data、Category属性完全一致,系统就会直接返回缓存的旧实例,完全忽略新传入Intent里的Extra内容差异。
- 当前实现中,创建PendingIntent时requestCode固定传0,目标Activity固定为DisplayActivity,无其他自定义Intent属性做区分,因此第一次匹配到London时创建的带"London" Extra的PendingIntent,会被系统永久缓存。后续不管Service里的placeName更新成Bristol还是其他值,只要按照原参数创建PendingIntent,系统都会直接返回第一次缓存的旧实例,根本不会加载新的Extra数据。
- 该缓存是系统级的,和应用进程生命周期无关:停止、重启应用都不会清除该缓存,只有卸载应用时系统才会删除对应包名下的所有PendingIntent缓存,这就解释了为什么只有重装应用才能临时恢复正常。
修复实现方案
1. 调整Service端的PendingIntent创建逻辑
从两个维度打破PendingIntent的复用规则,确保每次传递的地点名称都能被正确加载:
- 为每次创建的PendingIntent生成唯一的requestCode,从根源上避免不同地点的PendingIntent被判定为同一个缓存实例。可以使用当前地点的标识(placeNumber、placeName的哈希值,或者位置匹配的累计次数)作为requestCode,保证每一次匹配的requestCode都不重复。
- 为PendingIntent补充符合系统要求的标记位:
- 必须兼容targetSdk 31及以上的强制要求,添加
FLAG_IMMUTABLE(无需外部修改PendingIntent内容时使用)或FLAG_MUTABLE标记,否则应用会直接崩溃。 - 搭配
FLAG_UPDATE_CURRENT标记,作用是当系统匹配到已存在的同规格PendingIntent时,自动用新传入的Intent的Extra内容更新旧实例,避免数据滞留。
- 必须兼容targetSdk 31及以上的强制要求,添加
- 修正后的代码示例:
void createPopUpNotification(){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { Log.v("sTag","Create notification: placeNumber = "+ placeNumber+" placeName = "+placeName); String NOTIFICATION_CHANNEL_ID = "notifychannel"; Intent goShow = new Intent(this, DisplayActivity.class); goShow.putExtra("plname",placeName); // 生成唯一requestCode 避免PendingIntent复用 int uniqueRequestCode = (placeNumber + placeName).hashCode(); // 适配不同版本的PendingIntent标记位 int pendingFlags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT : PendingIntent.FLAG_UPDATE_CURRENT; PendingIntent pendingIntent = PendingIntent.getActivity(this, uniqueRequestCode, goShow, pendingFlags); RemoteViews expandedView=new RemoteViews(getApplicationContext().getPackageName(),R.layout.expandednotify); expandedView.setTextViewText(R.id.placename,placeName); NotificationManager notificationManager = (NotificationManager) getSystemService( NOTIFICATION_SERVICE ) ; NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID); builder.setSmallIcon(R.mipmap.amjnotify); builder.setCustomContentView(expandedView); builder.setAutoCancel(true); builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC); builder.setPriority(NotificationCompat.PRIORITY_HIGH); builder.setFullScreenIntent(pendingIntent,true); NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "mychannel", NotificationManager.IMPORTANCE_HIGH); builder.setChannelId( NOTIFICATION_CHANNEL_ID ) ; channel.setSound(null, null); notificationManager.createNotificationChannel(channel); notificationManager.notify(123, builder.build()); } }
2. 补全Activity端的Intent接收逻辑
如果DisplayActivity在Manifest中配置了singleTop/singleTask/singleInstance等非标准启动模式,已存在的Activity实例被新的Intent启动时不会重复执行onCreate,此时需要重写onNewIntent()方法,确保能接收到最新的Intent数据:
@Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); // 将新Intent设置为Activity的有效全局Intent setIntent(intent); // 重新解析Extra数据 Bundle params = intent.getExtras(); if (params != null) { placeName = params.getString("plname"); Log.v("sTag","getParams: placeName="+placeName); } }
- 解析Extra时必须添加非空判断,避免Intent无对应key时触发空指针异常。
- 原代码中在
onCreate里执行的goShow.removeExtra()操作不需要保留,因为每次创建的都是全新的Intent实例,不存在需要清除旧Extra的场景。
内容的提问来源于stack exchange,提问作者Chris S
相关产品推荐
相关产品推荐

