1. 广播简介
BroadcastReceiver也就是“广播接收者”的意思,顾名思义,就是用来接收来自系统和应用中的广播,是一种广泛运用在应用程序之间传输信息的机制。在 Android 里面有各种各样的广播,例如当开机完成后系统会产生一条广播,接收到这条广播就能实现开机启动服务的功能,当网络状态改变时,系统会产生一条广播,接收到这条广播,就能及时的做出提示和保存数据等操作,当电池的电量改变的时候,系统会产生一条广播,接收到这条广播就能在电量低的时候告知用户,及时保存进度。
2.广播接收器的类型
2.1 普通广播(Normal Broadcast)
普通广播是开发者自身定义intent的广播(最常用)。发送一条默认的广播使用Context.sendBroadcast()方法,普通广播对于多个接受者来说是异步的,通常每个接受者都无需等待即可以接收到广播,接受者之间互相不会有影响,对于这种广播,接受者无法终止广播,即无法阻止其他接受者的接收动作。
使用步骤1:创建Receiver
public class MyReceiver extends BroadcastReceiver {
public MyReceiver() {
}
@Override
public void onReceive(Context context, Intent intent) {
String info = intent.getStringExtra("info");
Toast.makeText(context, info, Toast.LENGTH_LONG).show();
}
}
使用步骤2:在Manifest文件中注册
<receiver android:name=".MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.wxt.action.BROADCAST"></action>
</intent-filter>
</receiver>
使用步骤3:调用广播
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("com.wxt.action.BROADCAST");
intent.putExtra("info","广播广播啦啊");
MainActivity.this.sendBroadcast(intent);
}
});
}
}
2.2 有序广播
有序广播是指发送出去的广播被广播接收者按照先后顺序接收,有序是针对广播接收者而言的。发送一个有序广播使用Context.sendOrderedBroadcast( )方法,有序广播比较特殊,它每次只发1个到优先级较高的接受者那里,然后由优先级较高的接受者再传播到优先级别低的接受者那里,优先级高的接受者有能力终止这个广播。
广播接受者接受广播的优先级顺序是按照Priority属性值从大到小排序,数越大优先级越高(取值范围:-1000~10000),如果Priority属性相同则动态注册的广播优先。
先接收的广播接收者可以对广播进行截断,即后接收的广播接收者不再接收到此广播;先接收的广播接收者可以对广播进行修改,那么后接收的广播接收者将接收到被修改后的广播。
使用sendOrderedBroadcast方法有序广播的时候,需要一个权限参数,如果为null则表示不要求接收者声明指定的权限,如果不为null,则表示接收者要接收此广播,需声明指定权限,这样做是从安全角度考虑的,例如系统的短信就是有序的广播的形式,一个应用可能是具有拦截垃圾短信的功能,当短信到来的时候它可以先接收到短信,必要时候终止广播的传递,这样的软件就必须声明接收短信的权限。
使用步骤1:创建第一个OrderReceiver
public class OrderReceiver extends BroadcastReceiver {
public OrderReceiver() {
}
@Override
public void onReceive(Context context, Intent intent) {
String info = intent.getStringExtra("info")+" receiver 01";
Toast.makeText(context,info,Toast.LENGTH_LONG).show();
}
}
使用步骤2:创建第二个OrderReceiver
public class OrderReceiver2 extends BroadcastReceiver {
public OrderReceiver2() {
}
@Override
public void onReceive(Context context, Intent intent) {
String info = intent.getStringExtra("info")+" receiver 02";
Toast.makeText(context,info,Toast.LENGTH_LONG).show();
}
}
使用步骤3:在manifest文件中注册
<receiver android:name=".OrderReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="100">
<action android:name="com.wxt.action.ORDER_BROADCAST"></action>
</intent-filter>
</receiver>
<receiver android:name=".OrderReceiver2"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="200">
<action android:name="com.wxt.action.ORDER_BROADCAST"></action>
</intent-filter>
</receiver>
使用步骤4:调用广播
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("com.wxt.action.ORDER_BROADCAST");
intent.putExtra("info","我是有序广播");
MainActivity.this.sendOrderedBroadcast(intent,null);
}
});
}
}
2.3 粘性广播
当处理完之后的intent,依然存在,直到将它去掉。发送粘性广播使用:sendStickBroadcast(intent)。由于在Android5.0 & API 21中已经失效,所以不建议使用,在这里也不作过多的总结。
2.4 系统广播
Android中内置了多个系统广播:只要涉及到手机的基本操作(如开机、网络状态变化、拍照等等),都会发出相应的广播。
每个广播都有特定的Intent - Filter(包括具体的action),Android系统广播action如下:
注意:当使用系统广播的时候,只需要在注册广播接收者时定义相关的action即可,并不需要手动发送广播,当系统有相关操作的时候会自动进行系统广播。
2.5 应用内广播
Android中的广播可以跨App直接通信(exported对于有intent-filter情况下默认值为true),可能会出现其他App针对性发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收广播并处理,也有可能出现其他App注册与当前App一致的intent-filter用于接收广播,获取广播具体信息,即会出现安全性和效率性的问题。针对此问题,可以使用应用内广播。App应用内广播可理解为一种局部广播,广播的发送者和接收者都同属于一个App,相比于全局广播(普通广播),App应用内广播优势体现在:安全性高和效率高。
2.5.1 使用方法1:将全局广播设置为局部广播
(1)注册广播时将exported属性设置为false,使得非本App内部发出的此广播不被接收
(2)在广播发送和接收时,增设相应权限permission,用于权限验证
(3)发送广播时指定该广播接收器所在的包名,此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中,通过intent.setPackage(packageName)指定报名
2.5.2 使用方法2:使用封装好的LocalBroadcastManager类
使用方式上与全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将参数的context变成了LocalBroadcastManager的单一实例。对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册,不能静态注册。
//注册应用内广播接收器
//步骤1:实例化BroadcastReceiver子类 & IntentFilter mBroadcastReceiver
mBroadcastReceiver = new mBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
//步骤2:实例化LocalBroadcastManager的实例
localBroadcastManager = LocalBroadcastManager.getInstance(this);
//步骤3:设置接收广播的类型
intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);
//步骤4:调用LocalBroadcastManager单一实例的registerReceiver()方法进行动态注册
localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);
//取消注册应用内广播接收器
localBroadcastManager.unregisterReceiver(mBroadcastReceiver);
//发送应用内广播
Intent intent = new Intent();
intent.setAction(BROADCAST_ACTION);
localBroadcastManager.sendBroadcast(intent);
3.注册广播的两种方式(静态注册和动态注册)
3.1 静态注册
静态注册是指是指在AndroidManifest.xml文件中配置,当此App首次启动时,系统会自动实例化mBroadcastReceiver类,并注册到系统中。
<receiver
android:enabled=["true" | "false"]
//此broadcastReceiver能否接收其他App的发出的广播
//默认值是由receiver中有无intent-filter决定的:如果有intent-filter,默认值为true,否则为false
android:exported=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
//继承BroadcastReceiver子类的类名
android:name=".mBroadcastReceiver"
//具有相应权限的广播发送者发送的广播才能被此BroadcastReceiver所接收;
android:permission="string"
//BroadcastReceiver运行所处的进程
//默认为app的进程,可以指定独立的进程
//注:Android四大基本组件都可以通过此属性指定自己的独立进程
android:process="string" >
//用于指定此广播接收器将接收的广播类型
//本示例中给出的是用于接收网络状态改变时发出的广播
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
注册示例:
<receiver android:name=".MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.wxt.action.BROADCAST"></action>
</intent-filter>
</receiver>
3.2 动态注册
动态注册是指在代码中通过调用Context的registerReceiver()方法进行动态注册BroadcastReceiver
@Override
protected void onResume() {
super.onResume();
//实例化BroadcastReceiver子类 & IntentFilter
mBroadcastReceiver mBroadcastReceiver = new mBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
//设置接收广播的类型
intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);
//调用Context的registerReceiver()方法进行动态注册
registerReceiver(mBroadcastReceiver, intentFilter);
}
//注册广播后,要在相应位置记得销毁广播
//即在onPause() 中unregisterReceiver(mBroadcastReceiver)
//当此Activity实例化时,会动态将MyBroadcastReceiver注册到系统中
//当此Activity销毁时,动态注册的MyBroadcastReceiver将不再接收到相应的广播。
@Override
protected void onPause() {
super.onPause();
//销毁在onResume()方法中的广播
unregisterReceiver(mBroadcastReceiver);
}
特别注意:
动态广播最好在Activity的onResume( )中注册,onPause( )中注销;原因是动态广播有注册就需要有注销,否则会导致内存泄漏,在onResume()注册、onPause()注销是因为onPause()在App死亡前一定会被执行,从而保证广播在App死亡前一定会被注销,从而防止内存泄露。
3.3 两种注册方式对比
4.实现原理
Android中的广播使用了设计模式中的观察者模式:基于消息的发布/订阅事件模型。广播的原理示意图如下
原理描述:
(1)广播接受者通过Binder机制在AMS注册
(2)广播发送者通过Binder机制想AMS发送广播
(3)AMS根据广播发送者的要求,在已注册列表中,寻找合适的广播接受者,寻找依据时IntentFilter/Permission
(4)AMS将广播发送到合适的广播接受者相应的消息循环队列中
(5)广播接受者通过消息循环拿到此广播,并回调onReceive( )
注意:广播发送者和广播接受者的执行时异步的,发出去的广播不会关心有无接受者接收,也不确定接受者到底合适才能接收到
5.注意事项
对于不同注册方式的广播接收器回调OnReceive(Context context,Intent intent)中的context返回值是不一样的:
(1)对于静态注册(全局+应用内广播),回调onReceive(context, intent)中的context返回值是:ReceiverRestrictedContext;
(2)对于全局广播的动态注册,回调onReceive(context, intent)中的context返回值是:Activity Context;
(3)对于应用内广播的动态注册(LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Application Context。
(4)对于应用内广播的动态注册(非LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Activity Context;