一、基本概念
1.1 实现广播接收者
首先,我们需要创建一个广播接收者,继承于BroadcastReceiver
并重写它的onReceive
方法。
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {}
}
在创建完接收者之后,还需要进行注册,告诉系统有这个监听者。广播注册的方式分为:静态注册和动态注册。
静态注册
静态注册在AndroidManifest.xml
中进行指定。
<receiver android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
android:name="string"
android:permission="string"
android:process="string" >
. . .
</receiver>
exported
:exported
决定了 广播接收者所能收到广播的范围,假如为false
,那么只有同一个App
,或者userId
相同的App
发出的广播它才能够收到,并不是指同一个进程。exported
一般情况下默认为false
,唯一例外的是假如设置了intent-filter
,那么默认值为true
。对于静态注册的广播,在Android 3.1
之后,应用如果没有启动并且Intent
中包含了FLAG_INCLUDE_STOPPED_PACKAGES
属性,那么会先调起应用,否则在应用没有启动的情况下将无法收到广播。permission
:如果设置了permission
,那么只有 具有相应权限的广播发送方 发送的广播才能被此BroadcastReceiver
接收。Android
广播的权限机制是双向的,即我们既可以 要求发送者具有权限,也可以 要求接收者具有权限,才能完成端到端的通信过程,这里就是 要求发送者具有权限。process
:运行所处的进程,默认为App
的进程。
动态注册
动态注册的广播无需在AndroidManifest.xml
进行声明,在代码中进行注册和注销即可。
//注册广播。
registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
//注销广播。
unregisterReceiver(BroadcastReceiver receiver);
1.2 发送广播
广播的发送者通过Intent
将其意图发送出去,系统找到匹配的接收者,发送广播的一般方式如下:
Intent intent = new Intent();
intent.setAction(INTENT_ACTION);
sendBroadcast(intent);
1.3 广播类型
广播可以分为以下几类:无序广播、有序广播和粘性广播。
1.3.1 无序广播
无序广播指的是所有广播接收者收到广播的顺序是没有规律的。
1.3.2 有序广播
有序广播指的是发送出的广播被BroadcastReceiver
按照priority
从大到小的顺序接收,当priority
相同时,动态广播优先静态广播,发送有序广播的方式为:
sendOrderedBroadcast(intent, receiverPermission, ...)
对于有序广播有一个特点,先接收的BroadcastReceiver
具有拦截广播的权利,拦截的方法为onReceive
方法中调用abortBroadcast()
方法。
1.3.3 粘性广播
已经废弃,它是用来处理先收到广播然后才注册的情况。
1.4 应用内广播
假如exported
属性为true
,那么是允许两个不同应用通过广播进行通信的。就可能出现 安全隐患:
- 其他
App
可能会针对性地发出与当前App
的intent-filter
相匹配的广播,导致当前App
不断接收到广播并处理。 - 其他
App
可以注册与当前App
相匹配的intent-filter
,从而获取广播具体信息。
为了避免出现以上的安全问题,有以下的解决方法:
- 设置
exported
属性为false
。 - 设置权限。
- 发送广播时,指定具体的包名。
假如我们的广播只需要在应用内部通信,那么可以采用封装好的LocalBroadcastManager
类,用于解决安全问题。
传统的广播是通过Binder
来实现的,而LocalBroadcastManager
则是采用Handler
的方式。当注册广播的时候,其实将Receiver
添加到单例对象LocalBroadcastManager
维护的列表当中,发送消息的时候,通过Receiver
所关联的action
找到它,最后回调它的onReceive
方法。
因此,只有通过LocalBroadcastManager
注册的BroadcastReceiver
才能收到通过LocalBroadcastManager
发出的广播。
具体的代码实现可以参考这篇文章 LocalBroadcastManager 的实现原理,还是 Binder?。
二、一些需要注意的点
2.1 权限问题
通过权限可以也可以解决我们之前谈到的安全问题,Broadcast
的权限是双向的。
2.1.1 要求接收者具有权限
这种方式 用于防止广播信息泄露。
在 发送者 的AndroidManifest.xml
中定义权限。
<permission android:name = "com.android.permission.RECV_XXX"/>
发送者在发送广播的时候,采用带有权限的接口进行发送。
sendBroadcast("com.android.XXX_ACTION", "com.android.permission.RECV_XXX");
接收者 如果希望能收到广播,那么需要在它的AndroidManifest.xml
进行声明使用该权限。
<uses-permission android:name="com.android.permission.RECV_XXX"></uses-permission>
2.1.2 要求发送者具有权限
这种方式 用于防止外部应用恶意地发送广播,导致接收者一直在处理。
在 接收者 的AndroidManifest.xml
中定义权限。
<permission android:name="com.android.SEND_XXX"/>
在接收者的AndroidManifest.xml
声明BroadcastReceiver
的时候,通过permission
字段指定权限。
<receiver android:name=".XXXReceiver"
android:permission="com.android.permission.SEND_XXX">
<intent-filter>
<action android:name="com.android.XXX_ACTION" />
</intent-filter>
</receiver>
发送者 如果希望广播能被该接收者收到,那么需要在AndroidManifest.xml
中声明使用该权限。
<uses-permission android:name="com.android.permission.SEND_XXX“></users-permission>
2.2 ANR
在BroadcastReceiver
方法中,不要进行耗时的操作,超过10s
会发生BroadcastQueue Timeout
的ANR
异常。
2.3 onReceive 传入的 Context
静态注册的
BroadcastReceiver
,其Context
是android.app.ReceiverRestrictedContext
。动态注册的普通
BroadcastReceiver
,与调用registerReceiver
方法的Context
有关,如果是通过Application Context
注册的,那么Context
是Application Context
,如果是Activity Context
,那么其Context
是Activity Context
。LocalBroadcastManager
动态注册的BroadcastReceiver
,其Context
是Application Context
。
三、参考文章
Android Broadcast 和 BroadcastReceiver 的权限限制
Android 总结篇系列:Android 广播机制
LocalBroadcastManager 的实现原理,还是 Binder?