Android广播,你可能不知道的事

3,547 阅读4分钟

清单广播限制

从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对象,当任务执行完毕,还需要调用PendingResultfinish()通知系统进程的后台任务执行完毕,此时系统会根据情况决定是否杀死进程来释放资源。