Android 开机广播接收不到
背景
一般来说,我们都是用的监听android.intent.action.BOOT_COMPLETED。
但凡稍有些经验的开发者都知道,这个广播很慢,非常慢。因为它是一个有序广播,根据优先级来的,而且监听这个广播的apk又非常多。打个log感受一下,这个广播开始到结束在我司的机器上持续了30s!
关键是你把优先级调高了,即便你是前几个收到android.intent.action.BOOT_COMPLETED的,从开机动画走完,锁屏界面跳出来 到你的apk收到这个广播,大概还是有个5s左右的时间。
原因是android.intent.action.BOOT_COMPLETED发出来的时候就不是最早的。它前面还有
android.intent.action.LOCKED_BOOT_COMPLETED,android.intent.action.MEDIA_MOUNTED等。
要等这几个广播处理完了,才轮到android.intent.action.BOOT_COMPLETED。
我们的目的是让我们的apk尽早起来(一般都是收了广播,然后receiver的onReceive去start我们的service)。
那么我们有以下几种处理办法:
1.接收android.intent.action.BOOT_COMPLETED,调高优先级。
2.接收android.intent.action.MEDIA_MOUNTED,因为这个广播一方面比android.intent.action.BOOT_COMPLETED发送的要早,一方面监听它的apk比较少。
3.直接在AMS里面去start我们的service,过于粗暴且不通用。
4.自定义开机广播,这样监听这个广播的apk只有我们自己,并且尽早的发出这个广播。
我们要考虑的问题:
1.如果监听android.intent.action.MEDIA_MOUNTED,这个广播是挂载存储的时候发的,开机的时候挂载到手机的存储区(storage/emulated/0)会发一下,后面如果插入sdcard也会发。要评估多次触发广播是否会对自己的apk业务逻辑造成影响(以免自己本来stop了的service又被叫起来)。
2.自定义开机广播,这个广播发出来的时机。需要考虑system是否Ready,这个一般都达到了,大家写也会注意。还有点需要考虑,apk的AndroidManifest.xml的组件信息是何时导入的,如果广播发出来的时候,我们的apk的receiver信息还没被PackageManagerService导入,一样是收不到的,这个坑我自己在Android P上面踩到了。后面会详细说。
先直接上可行的结论:
Android O自定义开机广播
frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java
finishBooting()方法里:
UserController.sendBootCompletedLocked(
new IIntentReceiver.Stub() {
@Override
public void performReceive(Intent intent, int resultCode,
String data, Bundle extras, boolean ordered,
boolean sticky, int sendingUser) {
synchronized (ActivityManagerService.this) {
requestPssAllProcsLocked(SystemClock.uptimeMillis(),
true, false);
}
}
});
scheduleStartProfilesLocked();
//potter add
Intent customIntent=new Intent("com.honeywell.intent.action.BOOT_COMPLETED");
customIntent.setPackage("com.honeywell.ezreceiver");
mContext.sendBroadcast(customIntent);
//potter end
有人可能会问,这样只有一个apk能收到,如何让所有apk都收到。按下面这么写即可。
//potter add
Intent customIntent=new Intent("com.honeywell.intent.action.BOOT_COMPLETED");
customIntent. addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
mContext.sendBroadcast(customIntent);
//potter end
可能又有人会问,这样apk收到的顺序没法控制,那么按下面这么写:
//potter add
Intent customIntent=new Intent("com.honeywell.intent.action.BOOT_COMPLETED");
customIntent. addFlags(Intent.FLAG_RECEIVER_NO_ABORT | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
mContext. sendOrderedBroadcast(intent, null);
//potter end
我们多给加上一个Intent.FLAG_RECEIVER_NO_ABORT的Flag,让这个有序广播不能被abort掉。实际上系统里面的android.intent.action.BOOT_COMPLETED这个广播就是这两个flag。
这么写,功能已经实现了。但是从设计的角度很不好。因为这么写作为一个在System发出去的广播并没有加权限,这是不符合android的规范的。
我们追下源码:
sendBroadcast>>>
ContextImpl.java的sendBroadcast>>>
ActivityManagerService.java的broadcastIntent>>>
ActivityManagerService.java的broadcastIntentLocked
在AMS的broadcastIntentLocked方法里的