Broadcast的Intent中塞入INTENT_NEW_TASK引发的兼容性问题

时间:2021-08-30 05:27:41

0. 起源

在做通知栏时,因为需要做点击通知栏做一些非启动Activity的操作,因此需要通过如下代码接受点击通知栏事件的广播

Intent clickIntent = new ntent(mContext,NotificationClickReceiver.class);
PendingIntent contentIntent = PendingIntent.getBroadcast(mContext, STOP_NOTIFICATION_ID,clickIntent,PendingIntent.FLAG_CANCEL_CURRENT);
mNotificationBuilder.setContentIntent(contentIntent); I

这样的代码本身是没有问题的,但是因为在htc的某个rom下会出现在kill掉app后,通过startForeground启动的通知栏在点击的时候,无法正常接受和发送广播(原因是因为点击后会重新启动一个新的进程运行应用)。

具体点击跳转到stack-overflow查看.

因此需要在clickIntent中加入

clickIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP| Intent.FLAG_ACTIVITY_NEW_TASK);

1. 引发的问题

在4.4以下的机子下会出现crash,日志如下

java.lang.RuntimeException:Unable to start service com.flamingo.gpgame.service.GPDownloadService@4197e5e0 with Intent { cmp=com.flamingo.gpgame/.service.GPDownloadService (has extras) }: java.lang.IllegalArgumentException: Can't use FLAG_RECEIVER_BOOT_UPGRADE here

具体就是因为发送的广播Intent不能有FLAG_RECEIVER_BOOT_UPGRADE这个flags,我们的app不具备这种flags广播发送的权限。

2. 原因探寻

在我们的Intent中其实并没有添加FLAG_RECEIVER_BOOT_UPGRADE这个flag,只是添加了Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP| Intent.FLAG_ACTIVITY_NEW_TASK这三种flag,通过搜索资料发现,原来是因为谷歌工程师粗心大意把Intent.FLAG_ACTIVITY_NEW_TASKIntent. FLAG_RECEIVER_BOOT_UPGRADE的常量混淆了,系统就误认为我们发送了Intent. FLAG_RECEIVER_BOOT_UPGRADE这个广播。

具体点击跳转到stack-overflow查看.

通过分析源码也验证这上面的说法,在android-15的源码中

   /**
* <strong>Do not use this flag unless you are implementing your own
* top-level application launcher.</strong> Used in conjunction with
* {@link #FLAG_ACTIVITY_NEW_TASK} to disable the
* behavior of bringing an existing task to the foreground. When set,
* a new task is <em>always</em> started to host the Activity for the
* Intent, regardless of whether there is already an existing task running
* the same thing.
*
* <p><strong>Because the default system does not include graphical task management,
* you should not use this flag unless you provide some way for a user to
* return back to the tasks you have launched.</strong>
*
* <p>This flag is ignored if
* {@link #FLAG_ACTIVITY_NEW_TASK} is not set.
*
* <p>See
* <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
* Stack</a> for more information about tasks.
*/

public static final int FLAG_ACTIVITY_MULTIPLE_TASK = 0x08000000;

public static final int FLAG_RECEIVER_BOOT_UPGRADE = 0x08000000;

发现FLAG_ACTIVITY_MULTIPLE_TASKFLAG_RECEIVER_BOOT_UPGRADE的常量值是一致的,而注释中也提到FLAG_ACTIVITY_NEW_TASK是会覆盖FLAG_ACTIVITY_MULTIPLE_TASK的值,因此可以猜想处理中应该是FLAG_ACTIVITY_NEW_TASK包含了FLAG_ACTIVITY_MULTIPLE_TASK,系统也会认为赋值了FLAG_ACTIVITY_NEW_TASK的flags会拥有FLAG_RECEIVER_BOOT_UPGRADE的属性。

上述stack-overflow提到在android-19以后的版本修复了这个问题,查看源码发现确实是这样的,常量已经改变了值,以防冲突。

public static final int FLAG_RECEIVER_BOOT_UPGRADE = 0x02000000;

3.解决方式

因为在实际应用中既要解决htc那个rom的奇葩问题,又要保证Android 4.4以下的机子正常运行,只能通过版本判断来区分是否添加flag(幸好Htc的rom是基于Android 4.4)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
clickIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
}

当然,保险的方法是在平时的使用中不要在广播的Intent中添加Intent.FLAG_ACTIVITY_NEW_TASK,最保险的就是连Intent.FLAG_ACTIVITY_XX的值都不要添加。