清单广播限制
从Android O版本(Android 8.0, API 26)开始,在AndroidManifest.xml注册的广播接收器,无法接收隐式广播。
既然无法接收隐式广播,那么就发送显式广播,例如可以在发送广播的时候指定接收者的包名
Intent intent = new Intent("some_action");
// 指定接收者的包名发送显式广播
intent.setPackage("receiver_package");
sendBroadcast(intent);
如果你是系统开发人员,还可以通过添加一个flag突破这个限制
Intent intent = new Intent("some_action");
// 这个flag表示清单注册的广播接收器也能接收到隐式广播
intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
sendBroadcast(intent);
Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
对第三方应用不开放,然而有意思的是,第三方应用可以直接把这个flag替换为对应的值,也可以突破这个限制
Intent intent = new Intent("some_action");
// 第三方应用直接指定值
intent.addFlags(0x01000000);
sendBroadcast(intent);
为广播添加权限
为广播添加权限其实就是限制广播的发送者或者接收者,以达到过滤的目的。
限制接收者
假如现在要发送一个广播,我们并不希望所有人都能接收这个广播,我们可以考虑在使用Context.sendBroadcast()
或Context.sendOrderedBroadcast()
发送的时候附带一个权限参数。我们可以使用系统的权限,也可以重新定义一个权限。
假如现在要发送一个带有自定义权限的广播,首先得在AndroidManifest.xml定义这个权限
<permission android:name="com.bxll.sender.receiver_permission"/>
然后在发送广播的时候,把这个权限作为参数传入方法
Intent intent = new Intent("some_action");
sendBroadcast(intent, "com.bxll.sender.receiver_permission");
sendBroadcast()
的第二个参数就是自定义的权限。当然,如果你希望清单注册的广播接收器能接收到这个广播,你还得按照上面所说做相应的处理。
为了接收这个广播,接收的应用必须在AndroidManifest.xml中申请使用这个权限
<uses-permission android:name="com.bxll.sender.receiver_permission" />
限制发送者
假设现在我们已经有一个广播接收器,我们并不希望所有人都能发送广播给这个接收器,我们可以在注册这个广播的接收器的时候添加一个权限。
同样,如果添加的权限是自定义权限,首先得在AndroidManifest.xml中定义
<permission android:name="com.bxll.receiver.sender_permission" />
然后在注册的时候使用这个权限。我们知道广播接收器的注册有两个方式,一个是动态注册,一个是静态注册。
如果是静态注册,在AndroidManifest.xml中的代码如下
<receiver android:name=".MyReceiver"
android:permission="com.bxll.receiver.sender_permission">
<intent-filter>
<action android:name="com.bxll.reciever.action" />
</intent-filter>
</receiver>
如果是动态注册,例如在Activity中注册,代码如下
IntentFilter filter = new IntentFilter("com.bxll.reciever.action");
registerReceiver(receiver, filter, "com.bxll.receiver.sender_permission", null);
那么广播的发送者只要在AndroidManifest.xml中申请了这个权限,就可以发送广播给这个接收者
<uses-permission android:name="com.bxll.receiver.sender_permission" />
注意,发送广播的时候不需要再携带权限参数!!!!
小结
无论是在发送广播,还是在注册广播接收器时添加了权限,只需要在另一方的AndroidManifest.xml中申请这个权限即可,并不需要做额外处理。
广播接收器对宿主进程状态的影响
当一个广播接收器的onReceive()
在执行时候,系统认为这个广播接收器的宿主进程处理前台,并且会保持这个进程继续运行,除非在系统内存极度紧张的状态下才会杀死这个进程。
然而,当广播接收器的onReceive()
方法执行完毕后,并且宿主只有这个一个广播接收器在运行,那么系统认为这个宿主进程处理低优先级状态,并且很可能杀死这个进程来释放资源。
所以,我们不应该在onReceive()
创建一个后台线程用于处理任务(广播接收器在主线程中执行),因为宿主进程可能被杀死,后台线程会被终止。如果我们遇到了一定要在后台线程处理任务的情况,可以使用JobScheduler
来计划将来要执行的任务,或者调用goSync()
表明你需要更多时间在后台处理任务,这样系统就会知道进程需要继续执行任务,从而在正常的情况下不会杀死进程。
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
final PendingResult pendingResult = goAsync();
Task asyncTask = new Task(pendingResult);
asyncTask.execute();
}
private static class Task extends AsyncTask<String, Integer, String> {
private final PendingResult pendingResult;
private Task(PendingResult pendingResult) {
this.pendingResult = pendingResult;
}
@Override
protected String doInBackground(String... strings) {
// 这里执行耗时任务
return "some_result";
}
@Override
protected void onPostExecute(String s) {
// 通知系统任务执行完毕
pendingResult.finish();
}
}
}
在onReceive()
中调用goSync()
方法通知系统进程需要更多时间处理任务,goSync()
会返回一个PendingResult
对象,当任务执行完毕,还需要调用PendingResult
的finish()
通知系统进程的后台任务执行完毕,此时系统会根据情况决定是否杀死进程来释放资源。