一、基本概念
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?
网友评论