应用场景(常见的场景 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.
----------------
来源: http://www.bubuko.com/infodetail-3323108.html