1. 使用场景
- Android不同组件间的通信
- 多线程通信
- 与Android系统在特定情况下的通信
2.实现流程
- 从原理上看,Android中的广播使用了观察者模式,基于消息的发布/订阅事件模型;从实现角度看,Android中的广播将广播的发送者和接受者极大程度的解耦,方便系统集成,扩展性好。
- 广播接受者BroadCastReceiver通过Binder 机制向 AMS(Android Manager Service)进行注册
- 广播发送者通过Binder机制向 AMS发送广播
- AMS查找符合条件的(IntentFilter/Permission等)的BroadCastReceiver,将广播发送到BroadCastReceiver相应的循环队列中。
- 循环队列执行拿到广播后,回调BroadCastReceiver中的onReceive方法
- 由此看来,广播接受者和广播发送者分类别属于观察者模式中的订阅和发布两端,AMS属于中间的处理中心,广播接受者和发送者都是异步执行的
3.两种注册方式
(1)静态注册
AndroidManifest.xml
<receiver android:name=".MyBroadCastReceiver">
<intent-filter>
<action android:name="com.mybroadCastReceiver"></action>
</intent-filter>
</receiver>
MyBroadCastReceiver
public class MyBroadCastReceiver extends BroadcastReceiver {
private static final String TAG = "MyBroadCastReceiver";
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "广播启动了"+intent.getStringExtra("name"));
}
}
MainActivity
mStartBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent();
intent.setAction("com.mybroadCastReceiver");
intent.putExtra("name", "静态注册");
sendBroadcast(intent);
}
});
在Android8.0之前静态注册广播还是可以接收到广播的,但是在Android8.0开始,静态注册无法再接收到广播,需要修改MainActivity中的代码
mStartBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//AndroidManifest.xml中的action
Intent intent=new Intent("com.mybroadCastReceiver");
//第一个参数:包名 第二个参数:广播接收器的路径
intent.setComponent(new ComponentName("collect.pingshow.com.demo3","collect.pingshow.com.demo3.MyBroadCastReceiver"));
intent.putExtra("name", "静态注册");
sendBroadcast(intent);
}
});
(2)动态注册
MainActivity
//动态注册广播
@Override
protected void onResume() {
super.onResume();
myBroadCastReceiver = new MyBroadCastReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("com.mybroadCastReceiver");
registerReceiver(myBroadCastReceiver, filter);
}
//启动广播
case R.id.startBtn3:
Intent intent2 = new Intent();
intent2.setAction("com.mybroadCastReceiver");
intent2.putExtra("name", "动态注册");
sendBroadcast(intent2);
break;
动态注册广播后,还需要在程序退出时,将广播销毁,不然会导致内存泄漏
@Override
protected void onPause() {
super.onPause();
//解绑广播
unregisterReceiver(myBroadCastReceiver);
}
(3)动态广播最好在onResume()和onPause注销。在onResume()注册、onPause()销毁是因为app在死亡前一定会被执行onPause,就可以保证广播在App死亡前一定会被注销,防止内存泄漏。
- 不在onCreate()& onDestroy()或onStart() & onStop()中注册、注销是因为,当系统内存不足时,优先级更高的程序需要使用内存,要回收activity占用的资源,activity执行完onPause()方法就会销毁,onStop()、onDestroy()方法就不会执行。当再回到activity是从onCreate方法开始执行
- 假如我们将广播的注销放在onStop、onDestroy中的话,有可能activity被销毁后还未执行onStop、 onDestroy方法,但是现在广播还没有销毁掉,从而导致内存泄漏。但是onPause一定会执行,可以保证App死亡前广播一定会被注销
4.两种注册方式的区别
(1)静态注册:不受任何生命周期组件的影响,应用程序关闭后,如有广播来,程序仍会被系统调用。缺点:耗电、费内存
(2)动态注册:跟随组件的生命周期变化,程序退出,广播也就结束了
5.广播的类型
- 普通广播(Normal BroadCast)
- 系统广播(System BroadCast)
- 有序广播(Ordered BroadCast)
- 粘性广播(Sticky BroadCast)
- App应用内广播(Local BroadCast)
(1)普通广播
Intent intent = new Intent();
intent.setAction("com.mybroadCastReceiver");
sendBroadcast(intent);
若被注册了的广播接收者中注册时intentFilter的action与上述匹配,就会回调onReceive方法
(2)系统广播
当使用系统广播时,只需要在注册广播接受者时定义相关的action就可以,不需要手动发送广播,当系统有相关操作就会自动进行系统广播
(3)有序广播
发送出去的广播被广播接受者按照先后顺序进行接收
按照Priority属性的值从大到小排序的
Priority属性值相同的,动态注册的广播优先
有序广播的发送方式
Intent intent = new Intent();
intent.setAction("com.mybroadCastReceiver");
sendOrderedBroadcast(intent);
(4)粘性广播
由于在Android5.0 & API 21中已经失效,所以不建议使用
(5)App应用内广播
在Android中,广播默认是可以跨App(跨进程)直接通信。因为exported属性默认是true 由于广播默认是跨进程,同时广播是根据发送意图Intent和意图过滤器Intent-filter进行匹配判断,从而决定BroadcastReceiver的onReceiver方法是否回调,这可能会出现以下问题:如果其他App发出来的广播Intent与当前App的Intent-filter相匹配,由此就会导致当前App不断接受广播并处理。如果不法分子知道了App的Intent-filter,就有可能通过此漏洞进行一些不良信息的推送,会影响App的安全性
针对以上问题的解决方案
将全局广播设置为局部广播
1.注册广播时,将exported属性设置为false,使得非本app内部发出的此广播不被接受
2.在广播发送和接收时,增设相应的权限permission
<permission android:name="com.lgq.My_BroadCastReceiver"/>
<receiver
android:name=".MyBroadCastReceiver"
android:permission="com.lgq.My_BroadCastReceiver">
<intent-filter>
<action android:name="com.mybroadCastReceiver"></action>
</intent-filter>
</receiver>
使用本地广播
对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册,不能静态注册。
myBroadCastReceiver = new MyBroadCastReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("com.mybroadCastReceiver");
LocalBroadcastManager instance = LocalBroadcastManager.getInstance(this);
//注册广播
instance.registerReceiver(myBroadCastReceiver,filter);
//销毁广播
instance.unregisterReceiver(myBroadCastReceiver);