Boardcast Receiver的要点
1、广播的发送
2、广播接收者的静态注册与动态注册
3、广播接收者的注册过程
4、广播的发送过程
5、本地广播的使用
6、广播内部实现机制
从实现原理看上,Android中的广播使用了观察者模式,基于消息的发布/订阅事件模型。因此,从实现的角度来看,Android中的广播将广播的发送者和接受者极大程度上解耦,使得系统能够方便集成,更易扩展。具体实现流程要点粗略概括如下:
1.广播接收者BroadcastReceiver通过Binder机制向AMS(Activity Manager Service)进行注册;
2.广播发送者通过binder机制向AMS发送广播;
3.AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver(一般情况下是Activity)相应的消息循环队列中;
4.消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法。
在Android系统中,广播是一种在组件之间进行消息传递的方式,这些组件可以在同一个进程也可以在不同进程。当两个不同进程中的组件通过广播机制来传递消息是,广播机制就有点类似Binder进程间通信机制。
广播机制是一种基于消息发布和订阅的事件驱动模型,即广播发送者负责发布消息,广播接收者需要订阅消息,然后才能接收消息。
广播机制存在一个注册中心,它是由ActivityManagerService来担当的。广播接收者订阅消息的表现形式就是将自己注册到ActivityManagerService中,并且指定要接收的广播类型,有两种方式分别发送两种不同的广播:
通过mContext.sendBroadcast(Intent)或mContext.sendBroadcast(Intent, String)发送的是无序广播(后者加了权限);
通过mContext.sendOrderedBroadcast(Intent, String, BroadCastReceiver, Handler, int, String, Bundle)发送的是有序广播。
区别:
无序广播:所有的接收者都会接收事件,不可以被拦截,不可以被修改。
有序广播:按照优先级,一级一级的向下传递,接收者可以修改广播数据,也可以终止广播事件。
例子:
(1)无序广播的使用:
Intent intent = new Intent(); //设置intent的动作为com.example.broadcast,可以任意定义
intent.setAction("com.example.broadcast"); //发送无序广播 sendBroadcast(intent);
定义一个广播接收者,来接收这个广播事件。通过Toast的打印判断是否收到广播
public class MyReceiver extends BroadcastReceiver {
public MyReceiver() {
}
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"收到广播", Toast.LENGTH_SHORT).show();
}
}
在Manifest.xml中配置该接收者。
<receiver
android:name=".MyReceiver" >
<intent-filter>
<!-- 动作设置为发送的广播动作 -->
<action android:name="com.example.broadcast"/>
</intent-filter>
</receiver>
(2)有序广播的使用
和无序广播使用不同的是 通过 mContext.sendOrderedBroadcast(Intent, String, BroadCastReceiver, Handler, int, String, Bundle)和每个接收者设置优先级,就可以在小于自己优先级的接收者得到广播前,修改或终止广播。
Intent intent = new Intent();
//设置intent的动作为com.example.broadcast,可以任意定义
intent.setAction("com.example.broadcast");
//发送无序广播
//第一个参数:intent
//第二个参数:String类型的接收者权限
//第三个参数:BroadcastReceiver 指定的接收者
//第四个参数:Handler scheduler
//第五个参数:int 此次广播的标记
//第六个参数:String 初始数据
//第七个参数:Bundle 往Intent中添加的额外数据
sendOrderedBroadcast(intent, null, null, null, "这是初始数据", );
定义多个广播接收者,来接收这个广播事件。通过Toast的打印判断是否收到广播
public class MyReceiver1 extends BroadcastReceiver {
public MyReceiver1() {
}
@Override
public void onReceive(Context context, Intent intent) {
//获取广播中的数据(即得到 "这是初始数据" 字符串)
String message = getResultData();
Toast.makeText(context ,message ,Toast.LENGTH_SHORT).show(); //修改数据 setResultData("这是修改后的数据");
}
}
public class MyReceiver2 extends BroadcastReceiver {
public MyReceiver2() { }
@Override
public void onReceive(Context context, Intent intent) {
String message = getResultData(); Toast.makeText(context ,message ,Toast.LENGTH_SHORT).show();
//终止广播
abortBroadcast();
}
}
public class MyReceiver3 extends BroadcastReceiver {
public MyReceiver3() {
}
@Override
public void onReceive(Context context, Intent intent) {
String message = getResultData();
Toast.makeText(context ,message ,Toast.LENGTH_SHORT).show();
}
}
在Manifest.xml中配置该接收者。并设置优先级:MyReceiver1>MyReceiver2>MyReceiver3
<!-- 优先级相等的话,写在前面的receiver的优先级大于后面的 -->
<receiver
android:name=".MyReceiver1" >
<!-- 定义广播的优先级 -->
<intent-filter android:priority="1000">
<!-- 动作设置为发送的广播动作 -->
<action android:name="com.example.broadcast"/>
</intent-filter>
</receiver>
<receiver
android:name=".MyReceiver2" >
<!-- 定义广播的优先级 -->
<intent-filter android:priority="0">
<!-- 动作设置为发送的广播动作 -->
<action android:name="com.example.broadcast"/>
</intent-filter>
</receiver>
<receiver
android:name=".MyReceiver3" >
<!-- 定义广播的优先级 -->
<intent-filter android:priority="-1000">
<!-- 动作设置为发送的广播动作 -->
<action android:name="com.example.broadcast"/>
</intent-filter>
</receiver>
运行结果:MyReceiver1得到广播数据后打印“这是初始数据”,MyReceiver2接收到广播数据打印“这是修改后的数据”,MyReceiver3没有打印。
代码中动态注册
(1)在代码中实例化自定义的广播接收者
(2)实例化意图过滤器,并设置过滤的广播l类型
(3)使用Context的registerReceiver方法注册广播接收者
//new出上边定义好的BroadcastReceiver
MyBroadCastReceiver smsBroadCastReceiver = new MyBroadCastReceiver(); //实例化过滤器并设置要过滤的广播
IntentFilter intentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
//注册广播
myContext.registerReceiver(smsBroadCastReceiver,intentFilter, "android.permission.RECEIVE_SMS", null);
在Manifest.xml中静态注册
直接在Manifest.xml文件的<application>节点中配置广播接收者。
<receiver android:name=".MyBroadCastReceiver">
<!-- android:priority属性是设置此接收者的优先级(从-1000到1000) -->
<intent-filter android:priority="20">
<actionandroid:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
还要在<application>同级的位置配置可能使用到的权限两种注册广播的不同
第一种不是常驻型广播,也就是说广播跟随程序的生命周期。
第二种是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。
广播接收者的注册过程
广播的发送过程
- 广播发送至将一个特定类型的广播发送给ActivityManagerService
- ActivityManagerService接收到一个广播之后,首先找到与这个广播对应的广播接收者,然后将它们添加到一个广播调度队列中,最后ActivityManagerService所运行在的线程的消息队列发送一个类型BROADCAST_INTENT_MSG的消息。
- 当发送到ActivityManagerService所运行在的线程的消息队列中的BROADCAST_INTENT_MSG的消息被处理时,ActivityManagerService就会从广播调度队列中找到需要接收广播的广播接收者,并将对于的广播发送给他们所运行在的应用程序进程。
- 广播接收者所在的应用程序接收到ActivityManagerService发送过来的广播之后,并不是直接将收到的广播分发给广播接收者,而是将接收到的广播封装成一个消息,并且发送到主线程的消息队列中。当这个消息被处理了,应用程序进程才会将它所描述的广播发送给相应的广播接受者处理。
本地广播:
App应用内广播可以理解成一种局部广播的形式,广播的发送者和接收者都同属于一个App。实际的业务需求中,App应用内广播确实可能需要用到。同时,之所以使用应用内广播时,而不是使用全局广播的形式,更多的考虑到的是Android广播机制中的安全性问题。
相比于全局广播,App应用内广播优势体现在:
1.安全性更高;
2.更加高效。