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启动的通知栏在点击的时候,无法正常接受和发送广播(原因是因为点击后会重新启动一个新的进程运行应用)。
因此需要在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_TASK
和Intent. FLAG_RECEIVER_BOOT_UPGRADE
的常量混淆了,系统就误认为我们发送了Intent. FLAG_RECEIVER_BOOT_UPGRADE
这个广播。
通过分析源码也验证这上面的说法,在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_TASK
和FLAG_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
的值都不要添加。