详解Android广播机制
应用场景(常见的场景1)
(1)同一应用具有多个进程的不同组件之间的消息通信
a)不同应用间的组件之间的消息通信
b)与Android系统在特定情况下的通信,如:系统开机,网络变化等
(2)同一应用内同一组件的消息通信:显然扩展变量的作用域、接口回调、Handler-Message等方式都能更简单的实现。
(3)同一应用内的不同组件之间的消息通信(单个进程):对于简单的的情况,依靠接口的回调方式就可解决;而较为复杂的情况,更推荐直接使用EventBus等。
实现原理
设计模式与模型: Android中的广播使用了观察者模式,模型为 基于消息的发布/订阅事件模型。
从设计模式上讲,广播的发送者和接收者极大程度的解耦,使得系统方便集成,容易扩展
模型成员:
- 消息发布者(广播发布者)
- 消息订阅者(广播接收者)
- 消息中心(AMS,Activity Manager Service,一个Android系统中极其重要!的成分,以后我们会详细讲解)
此处我们扩展一下,观察者模式和发布订阅模式的关系
- 发布订阅模式属于广义上的观察者模式,前者时最常用的一种观察者模式的实现,且从解耦和重用角度上看更优于典型的观察者模式,在观察者模式中,观察者需要直接订阅目标事件,在目标发出内容改变的事件后,直接接收事件并作出响应。
- 发布订阅模式加入消息中心,实现发布者和订阅者的解耦:在发布订阅模式中,多了一个消息中心,一方面从发布者接收事件,另一方面向订阅者发布事件,订阅者需要从消息中心订阅事件。以此避免发布者和订阅者之间产生依赖关系。
实现流程
- 广播接收者BroadcastReceiver通过Binder机制向AMS(Activity Manager Service)进行注册;
- 广播发送者通过binder机制向AMS发送广播;
- AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver
- AMS将广播发送到上述符合条件的BroadcastReceiver相应的消息循环队列中
- BroadcastReceiver通过消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法广播发送者和广播接收者的执行是异步的,发出去的广播不会关心有无接收者接收,也不确定接收者到底是何时才能接收到。
BroadcastReceiver
自定义BroadcastReceiver,继承基类BroadcaseReceiver,实现抽象方法onReceive(context, intent)收到广播后,会自动回调onReceive(..)方法,通常,onReceive(..)方法会涉及到与其他组件的交互,如发送Notification,启动service等。默认情况,BroadcaseReceiver运行在UI线程,因此,onReceive(..)方法不能执行耗时操作,否则ANR
简单的自定义Demo:
MyBroadcastReceiver.java
//继承BroadcastReceiver基类 public class MyBroadcastReceiver extends BroadcastReceiver { private static final String TAG = "MyBroadcastReceiver"; @Override public void onReceive(Context context, Intent intent) { StringBuilder sb = new StringBuilder(); sb.append("Action: " + intent.getAction() + "\n"); sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n"); String log = sb.toString(); Log.d(TAG, log); Toast.makeText(context, log, Toast.LENGTH_LONG).show(); } }
BroadcastReceiver注册类型
1. 静态注册
- 在
AndroidManifest.xml
文件中通过<receiver>
进行注册 - 规则及实例说明:
<receiver //BroadcastReceiver子类的类名 android:name="string" //是否使用该BroadcastReceiver 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:permission="string" //broadcastReceiver运行所处的进程。 //默认为app的进程,可以指定独立的进程 //Android四大基本组件都可以通过此属性指定自己的独立进程 android:process="string"> //指定此广播接收器将用于接收特定的广播类型 //本例中给出的时系统开机后自身发出的广播 <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter> </receiver>
以上述静态方法注册的MyBroadcastReceiver
,在app
首次启动时,系统或自动实例化MyBroadcastReceiver
,并注册到系统中。
2. 动态注册
- 在代码中调用
Context.registerReceiver()
, - 典型写法示例如下:
public class MainActivity extends AppCompatActivity { public static final String BROADCAST_ACTION = "com.example.whd_alive"; private BroadcastReceiver mBroadcastReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //实例化MyBroadcastReceiver mBroadcastReceiver = new MyBroadcastReceiver(); //实例化IntentFilter IntentFilter intentFilter = new IntentFilter(); //设置接收广播的类型 intentFilter.addAction(BROADCAST_ACTION); //动态注册 registerReceiver(mBroadcastReceiver, intentFilter); } //销毁广播 //当此Activity实例化时,会动态将MyBroadcastReceiver注册到系统中 //当此Activity销毁时,动态注册的MyBroadcastReceiver将不再接收到相应的广播 @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(mBroadcastReceiver); } }
注:Android中所有与观察者模式有关的设计中,一旦涉及到register,必定在相应的时机需要unregister。因此,上例在onDestroy()回调需要unregisterReceiver(mBroadcastReceiver)。
广播发送及广播类型
广播发送,广播 这一实体本身以 intent 表示,广播的定义 = 相应广播intent的定义,广播的发送:通过广播发送者将此intent发送出去,根据不同类型的广播调用相对应的send方法。
广播的类型主要分为一下四类:
- Normal Broadcast(普通广播):通常调用sendBroadcast(Intent)(Intent, String)方法发送
- System Broadcast(系统广播):发生各种事件时,系统自动发送
- Ordered Broadcast(有序广播):调用sendOrderedBroadcast(Intent, String)方法发送
- Local Broadcast(本地广播):调用LocalBroadcastManager.sendBroadcast(intent)方法发送
- Sticky Broadcast(粘性广播):已弃用(API 21)
1. Normal Broadcast(普通广播)
开发者自定义的intent,以Context.sendBroadcast(),Context.sendBroadcastAsUser()等方法发送该intent。
Intent intent = new Intent(); intent.setAction(BROADCAST_ACTION); //最普通的发送方式 sendBroadcast(intent); //附带权限的发送方式,声明此权限的BroadcastReceiver才能接收此广播 sendBroadcast(intent,RECEIVER_PREMISSION); //以下两种不常见,是因为只有预装在系统映像中的程序才能使用,否则无法使用 //指明接收人的发送方式 sendBroadcastAsUser(intent,USER_HANDLER); //指明接收人以及对应权限的发送方式 sendBroadcastAsUser(intent,USER_HANDLER,RECEIVER_PREMISSION);
- 若被注册了的
BroadCastReceiver
注册的intentFilter
的action
与上述匹配,则会接收此广播,且顺序是无序的。如果发送时有相应的权限要求,则BroadCastReceiver
只有拥有相应的权限才能接受。
<receiver android:name=".MyBroadcastReceiver" android:permission="RECEIVER_PREMISSION"> <intent-filter> <action android:name="BROADCAST_ACTION"/> </intent-filter> </receiver>
2. System Broadcast(系统广播)
Android系统中内置了多个系统广播,只要涉及到手机的基本操作,基本上都会发出相应的系统广播。每个系统广播都具有特定的intent-filter,其中主要包括具体的action,系统广播发出后,将被相应的BroadcastReceiver接收。系统广播在系统内部当特定事件发生时,有系统自动发出。
3. Ordered Broadcast(有序广播))
发送出去的广播被BroadcastReceiver按照先后循序接收。有序广播的有序广播中的“有序”是针对广播接收者而言的
发送方式:
定义过程与普通广播一样,调用sendOrderedBroadcast(),同样也有对应的sendOrderedBroadcastAsUser()方法,只不过同样针对于预装在系统映像的应用。
特点
按顺序接收
允许优先级高的BroadcastReceiver截断广播。
允许优先级高的BroadcastReceiver修改广播
接受顺序
priority值不同:由大到小排序
priority值相同:动态注册优于静态注册
4. Local Broadcast(本地广播)
可以理解成一种局部广播的形式,广播的发送者和接收者都同属于一个App
方案2的具体实现:
使用封装好的LocalBroadcastManager类。
使用方式上与通常的全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将主调context变成了LocalBroadcastManager的单一实例。
对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册的ContextReceiver才有可能接收到(静态注册或其他方式动态注册的ContextReceiver是接收不到的)
代码示例如下:
//实例化MyBroadcastReceiver mBroadcastReceiver = new MyBroadcastReceiver(); //实例化IntentFilter IntentFilter intentFilter = new IntentFilter(); //得到LocalBroadcastManager实例 LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this); //设置接收广播的类型 intentFilter.addAction(BROADCAST_ACTION); //动态注册 localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter); //取消注册 localBroadcastManager.unregisterReceiver(mBroadcastReceiver);
不同注册方式的广播接收器回调onReceive(context, intent)中的context具体类型
- 静态注册(全局+本地): 回调onReceive(context, intent)中的context具体指的是ReceiverRestrictedContext
- 全局动态注册: 回调onReceive(context, intent)中的context具体指的是Activity Context;
- LocalBroadcastManager动态注册,回调onReceive(context, intent)中的context具体指的是Application Context。
出于安全考虑的广播使用最佳实践
如不需要向应用程序之外的组件发送广播,则可以使用支持库Support Library中LocalBroadcastManager发送和接收本地广播。如果许多应用程序清单中注册接收相同的广播,它会导致系统启动大量的应用程序,从而对设备性能和用户体验产生重大影响。为了避免这种情况,请使用动态注册而不是Manifest声明。有时,Android系统本身会强制使用上下文注册的接收器。例如,CONNECTIVITY_ACTION广播只允许动态注册。
onReceive(Context, Intent)运行在UI线程,不要进行耗时操作
如耗时操作必不可少,生成子线程。
不要使用隐含的意图传播敏感信息。这些信息可以被任何注册的应用程序读取。
解决方案 : permission / setPackage(String) / LocalBroadcastManager.
当注册一个BroadcastReceiver,任何应用程序都可以发送潜在的恶意广播到你的应用的BroadcastReceiver。
解决方案 : permission / android:exported = "false" / LocalBroadcastManager.
广播操作的命名空间是全局的。确保操作名称和其他字符串都是在您自己的名称空间中编写的,否则您可能会无意中与其他应用程序发生冲突。
不要从BroadcastReceiver开始活动,这么做会导致用户体验很差,特别是如果有不止一个BroadcastReceiver。相反,考虑使用Notification。
————————————————
版权声明:本文为CSDN博主「whd_Alive」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/whdAlive/article/details/80250261