LocalBroadcastManager原理分析及应用
来源:互联网 发布:司考倒计时软件 编辑:程序博客网 时间:2024/06/07 08:53
引言
Android页面或模块之间通信的方法有很多,如Intent传值、startActivityForResult、EventBus(RxBus)等,大家追求的无非是解耦以及高灵活性;我们自己的应用中使用了基于Android消息机制封装的一套通信体系,且不谈这些,今天的主角是本地广播。
本地广播是系统提供的一种应用内通信方式,它跟全局广播相比,其运行机制是不同的。全局广播是系统级别的,需要跨进程调用,而且作用域是整个设备,而本地广播的作用域只在当前应用之内,因此无需IPC的过程,本应用发送的本地广播,其他应用接收不到,其他应用发送的本地广播,本应用也接收不到。
本地广播的优点
- 本地广播作用于App内部,广播数据不会泄露,本应用也不会接收到其他应用的广播,因此安全性较高
- 本地广播是在应用内部执行,无需跨进程逻辑,与普通广播相比效率更高
- 使用简单,无需静态注册
原理分析
本地广播的原理也很简单,大概流程如下
- 首先需要接收广播的页面创建一个BroadcastReceiver(广播声明跟全局广播是一样的)并注册到一个应用内的本地广播接收器列表(注册方式跟全局广播不同,下文会详细说明)
- 某个页面利用Intent发送广播(发送方式跟全局广播不同,下文会详细说明)
- 本地广播接收器列表根据IntentFilter匹配得到可以接收该广播的BroadcastReceiver,然后匹配的广播接收器会响应广播(注意这里区分广播的异步与同步发送)
- 在适当时候反注册BroadcastReceiver,比如Activity的onDestroy()回调中
为了方便大家的使用,Android提供了LocalBroadcastManager类(姑且称之为本地广播管理器),该类帮我们封装了注册、反注册、广播发送等过程,接下来分析一下源码实现。
LocalBroadcastManager定义
首先LocalBroadcastManager定义为一个单例,方便App内全局使用。
private static LocalBroadcastManager mInstance;public static LocalBroadcastManager getInstance(Context context) { synchronized (mLock) { if (mInstance == null) { mInstance = new LocalBroadcastManager(context.getApplicationContext()); } return mInstance; }}private LocalBroadcastManager(Context context) { mAppContext = context; mHandler = new Handler(context.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_EXEC_PENDING_BROADCASTS: executePendingBroadcasts(); break; default: super.handleMessage(msg); } } };}
注意到LocalBroadcastManager的构造函数中创建了一个Handler实例,并且是运行在主线程的,它响应一种what为MSG_EXEC_PENDING_BROADCASTS
的消息,这是用来处理异步广播的,具体如何处理稍后说明。
两个内部类
对于广播接收器的注册过程,并非简单地把BroadcastReceiver进行注册,而是进行了简单的包装,这里涉及到LocalBroadcastManager的两个内部类,如下:
ReceiverRecord
private static class ReceiverRecord { final IntentFilter filter; final BroadcastReceiver receiver; boolean broadcasting; ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) { filter = _filter; receiver = _receiver; } @Override public String toString() { StringBuilder builder = new StringBuilder(128); builder.append("Receiver{"); builder.append(receiver); builder.append(" filter="); builder.append(filter); builder.append("}"); return builder.toString(); }}
顾名思义,ReceiverRecord简单封装了BroadcastReceiver和IntentFilter。
BroadcastRecord
private static class BroadcastRecord { final Intent intent; final ArrayList<ReceiverRecord> receivers; BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers) { intent = _intent; receivers = _receivers; }}
BroadcastRecord也很简单,封装了广播Intent以及能够匹配该Intent的接收器列表(这里并非直接是BroadcastReceiver,而是包装类ReceiverRecord)。
基于上述两个包装类,LocalBroadcastManager提供了如下两个HashMap用来存储注册的接收器
private final HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers = new HashMap<BroadcastReceiver, ArrayList<IntentFilter>>();private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap<String, ArrayList<ReceiverRecord>>();
此外还有一个叫mPendingBroadcasts的容器如下
private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<BroadcastRecord>();
它是用来存储待处理广播的列表,其元素为BroadcastRecord。
注册过程
注册过程源码如下,具体流程分析请看注释
/** * Register a receive for any local broadcasts that match the given IntentFilter. * * @param receiver The BroadcastReceiver to handle the broadcast. * @param filter Selects the Intent broadcasts to be received. * * @see #unregisterReceiver */public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { // 加锁同步 synchronized (mReceivers) { // 将BroadcastReceiver和IntentFilter封装成ReceiverRecord ReceiverRecord entry = new ReceiverRecord(filter, receiver); // BroadcastReceiver作为HashMap的key,注意一般来说注册时一个BroadcastReceiver对应一个IntentFilter。但有一种情况下同一个页面内使用同一个BroadcastReceiver,配合不同的IntentFilter注册多次;更极端情况是应用内只有一个BroadcastReceiver,使用不同的IntentFilter进行多次注册 ArrayList<IntentFilter> filters = mReceivers.get(receiver); if (filters == null) { filters = new ArrayList<IntentFilter>(1); mReceivers.put(receiver, filters); } filters.add(filter); // 一个IntentFilter可以对应多个action for (int i=0; i<filter.countActions(); i++) { String action = filter.getAction(i); ArrayList<ReceiverRecord> entries = mActions.get(action); if (entries == null) { entries = new ArrayList<ReceiverRecord>(1); mActions.put(action, entries); } entries.add(entry); } }}
反注册过程
对应的反注册过程如下,代码逻辑请看注释
/** * Unregister a previously registered BroadcastReceiver. <em>All</em> * filters that have been registered for this BroadcastReceiver will be * removed. * * @param receiver The BroadcastReceiver to unregister. * * @see #registerReceiver */public void unregisterReceiver(BroadcastReceiver receiver) { // 加锁同步 synchronized (mReceivers) { // 同一个BroadcastReceiver可能对应多个IntentFilter,移除以BroadcastReceiver为key的键值对 ArrayList<IntentFilter> filters = mReceivers.remove(receiver); if (filters == null) { return; } for (int i=0; i<filters.size(); i++) { IntentFilter filter = filters.get(i); // 每个IntentFilter可对应多个action for (int j=0; j<filter.countActions(); j++) { String action = filter.getAction(j); // 以action作为key ArrayList<ReceiverRecord> receivers = mActions.get(action); if (receivers != null) { for (int k=0; k<receivers.size(); k++) { // 移除包含目标BroadcastReceiver的ReceiverRecord if (receivers.get(k).receiver == receiver) { receivers.remove(k); k--; } } if (receivers.size() <= 0) { mActions.remove(action); } } } } }}
异步发送广播
异步发送广播利用了Android消息机制,发送完成后即可返回,广播会异步响应,通常情况下我们都是使用异步方式,否则会被阻塞。
/** * Broadcast the given intent to all interested BroadcastReceivers. This * call is asynchronous; it returns immediately, and you will continue * executing while the receivers are run. * * @param intent The Intent to broadcast; all receivers matching this * Intent will receive the broadcast. * * @see #registerReceiver */public boolean sendBroadcast(Intent intent) { synchronized (mReceivers) { // 提取出Intent中的action、type、data、scheme、categories等,稍后用于匹配 final String action = intent.getAction(); final String type = intent.resolveTypeIfNeeded( mAppContext.getContentResolver()); final Uri data = intent.getData(); final String scheme = intent.getScheme(); final Set<String> categories = intent.getCategories(); // debug仅用于打印log,可忽略 final boolean debug = DEBUG || ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); if (debug) Log.v( TAG, "Resolving type " + type + " scheme " + scheme + " of intent " + intent); // 首先根据广播Intent的action进行匹配,匹配不上则直接返回false ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction()); if (entries != null) { if (debug) Log.v(TAG, "Action list: " + entries); // receivers用来存储匹配上的接收器包装类ReceiverRecord ArrayList<ReceiverRecord> receivers = null; for (int i=0; i<entries.size(); i++) { ReceiverRecord receiver = entries.get(i); if (debug) Log.v(TAG, "Matching against filter " + receiver.filter); if (receiver.broadcasting) { if (debug) { Log.v(TAG, " Filter's target already added"); } continue; } // InterFilter的匹配,若匹配成功,返回值match>=0 int match = receiver.filter.match(action, type, scheme, data, categories, "LocalBroadcastManager"); if (match >= 0) { if (debug) Log.v(TAG, " Filter matched! match=0x" + Integer.toHexString(match)); if (receivers == null) { receivers = new ArrayList<ReceiverRecord>(); } // 匹配成功的接收器加入列表,并设置一个标志 receivers.add(receiver); receiver.broadcasting = true; } else { // 匹配失败在debug为true的情况下仅输出log if (debug) { String reason; switch (match) { case IntentFilter.NO_MATCH_ACTION: reason = "action"; break; case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break; case IntentFilter.NO_MATCH_DATA: reason = "data"; break; case IntentFilter.NO_MATCH_TYPE: reason = "type"; break; default: reason = "unknown reason"; break; } Log.v(TAG, " Filter did not match: " + reason); } } } // 匹配成功的接收器 if (receivers != null) { // 将上述匹配成功时设置的标志重新置为false for (int i=0; i<receivers.size(); i++) { receivers.get(i).broadcasting = false; } // 将匹配成功的接收器封装为BroadcastRecord,加入待处理广播列表 mPendingBroadcasts.add(new BroadcastRecord(intent, receivers)); // 利用Handler消息机制,发送消息,异步处理广播 if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) { mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS); } return true; } } } return false;}
可以看到异步广播最终利用Android消息机制切换到主线程执行广播接收过程,具体实现逻辑则是在LocalBroadcastManager的构造器中:
private LocalBroadcastManager(Context context) { mAppContext = context; mHandler = new Handler(context.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_EXEC_PENDING_BROADCASTS: executePendingBroadcasts(); break; default: super.handleMessage(msg); } } };}
最终调用方法为executePendingBroadcasts()
,稍后详解。
同步发送广播
同步发送广播比较简单,实现如下
/** * Like {@link #sendBroadcast(Intent)}, but if there are any receivers for * the Intent this function will block and immediately dispatch them before * returning. */public void sendBroadcastSync(Intent intent) { if (sendBroadcast(intent)) { executePendingBroadcasts(); }}
具体的广播执行也是在executePendingBroadcasts()
中。
执行广播
private void executePendingBroadcasts() { while (true) { BroadcastRecord[] brs = null; synchronized (mReceivers) { final int N = mPendingBroadcasts.size(); if (N <= 0) { return; } brs = new BroadcastRecord[N]; mPendingBroadcasts.toArray(brs); mPendingBroadcasts.clear(); } for (int i=0; i<brs.length; i++) { BroadcastRecord br = brs[i]; for (int j=0; j<br.receivers.size(); j++) { br.receivers.get(j).receiver.onReceive(mAppContext, br.intent); } } }}
上述代码逻辑比较简单,从mPendingBroadcasts中遍历执行每个BroadcastReceiver的onReceive方法,触发BroadcastReceiver声明页面的回调。在回调中,我们就可以实现自己的广播接收逻辑了。
本地广播的响应逻辑为
@Overridepublic void onReceive(Context context, Intent intent) { // 处理逻辑}
因此如果要传值的话则要依赖于Intent的能力。
注意事项
- 发送广播可以在主线程也可以在子线程
- 接收广播在哪个线程要看是异步广播还是同步广播。如果是异步广播,无论发送广播是在主线程还是在子线程,内部都会通过Handler消息机制切换到主线程执行。如果是同步广播,发送广播的线程和接收广播的线程为同一线程,此种方式会导致线程阻塞,使用时需谨慎。
- 如果广播接收在主线程的话,注意不要进行耗时操作
- 通过广播传值要依赖于Intent的传值方式及要求
下表是一个简单总结
应用场景
单对单通信
场景描述:一个页面向另一个页面发送消息,可以是相邻页面也可以不相邻
实现方式:使用特定的action注册BroadcastReceiver,并使用具有该action的Intent发送本地广播
单对多通信
场景描述:一个页面发送广播,多个页面可以接收
实现方式:每个页面都使用相同的action注册BroadcastReceiver即可
多对单通信
场景描述:一个页面可以接收到多个不同广播
实现方式:该页面使用多个不同的action(一个IntentFilter可以有多个action)注册BroadcastReceiver,这样就能接收到具有不同action的广播了
应用示例
以下示例有3个页面,分别为ActivityA、ActivityB、ActivityC,其中ActivityA和ActivityB分别注册了广播接收器,ActivityC发送广播,ActivityA和ActivityB都可以接收到广播,接收到广播之后取出传值显示在UI上。
代码中注释了子线程发送广播、同步发送广播等逻辑,同时还有Log输出,可以查看同步、异步的执行顺序以及发送广播和接收广播所在的线程信息。源代码很简单,如下。
OnReceiveBrodadcastListener接口
public interface OnReceiveBroadcastListener { void onReceive(Context context, Intent intent);}
LocalBroadcastReceiver类,继承自BroadcastReceiver
public class LocalBroadcastReceiver extends BroadcastReceiver { private OnReceiveBroadcastListener mListener; @Override public void onReceive(Context context, Intent intent) { if (mListener != null) { mListener.onReceive(context, intent); } } public LocalBroadcastReceiver(OnReceiveBroadcastListener listener) { mListener = listener; }}
ActivityA的实现
public class ActivityA extends AppCompatActivity { private static final String TAG = "LocalBroadcastManager"; private TextView tv_a; private Button btn_a; private LocalBroadcastReceiver mReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_a); Log.d(TAG, "main thread id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName()); tv_a = (TextView) findViewById(R.id.tv_a); btn_a = (Button) findViewById(R.id.btn_a); btn_a.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(ActivityA.this, ActivityB.class); startActivity(intent); } }); mReceiver = new LocalBroadcastReceiver(new OnReceiveBroadcastListener() { @Override public void onReceive(Context context, Intent intent) { // 注意:异步消息是在主线程处理的,务必避免耗时操作 // 如果接收多个广播,可以根据action分别处理 String action = intent.getAction(); Log.d(TAG, "A onReceive=== thread id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName()); Log.d(TAG, "A received broadcast,action=" + action); // 取出广播传值 String name = intent.getStringExtra("NAME"); tv_a.setText(name); } }); IntentFilter filter = new IntentFilter(); // 一个IntentFilter可以配置多个action filter.addAction("com.aspook.localbroadcast_A"); filter.addAction("com.aspook.localbroadcast_AA"); LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter); // 同一个BroadcastReceiver可以注册多次,极端情况下,全局可以只有一个// IntentFilter filter2 = new IntentFilter();// filter2.addAction("com.aspook.localbroadcast_AAA");// LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter2); } @Override protected void onDestroy() { super.onDestroy(); LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver); }}
ActivityB的实现
public class ActivityB extends AppCompatActivity { private static final String TAG = "LocalBroadcastManager"; private TextView tv_b; private Button btn_b; private LocalBroadcastReceiver mReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_b); tv_b = (TextView) findViewById(R.id.tv_b); btn_b = (Button) findViewById(R.id.btn_b); btn_b.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(ActivityB.this, ActivityC.class); startActivity(intent); } }); mReceiver = new LocalBroadcastReceiver(new OnReceiveBroadcastListener() { @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "B onReceive=== thread id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName()); Log.d(TAG, "B received broadcast,action=" + intent.getAction()); Bundle bundle = intent.getExtras(); int age = bundle.getInt("AGE"); tv_b.setText(age + ""); } }); IntentFilter filter = new IntentFilter(); // 允许定义一个action数组,以接收不同的消息 filter.addAction("com.aspook.localbroadcast_B"); filter.addAction("com.aspook.localbroadcast_BB"); filter.addAction("com.aspook.localbroadcast_AA"); LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter); } @Override protected void onDestroy() { super.onDestroy(); LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver); }}
ActivityC的实现
public class ActivityC extends AppCompatActivity { private static final String TAG = "LocalBroadcastManager"; private TextView tv_c; private Button btn_c; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_c); tv_c = (TextView) findViewById(R.id.tv_c); btn_c = (Button) findViewById(R.id.btn_c); btn_c.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 在主线程发送本地广播 Log.d(TAG, "send broadcast=== thread id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName()); Intent intent = new Intent(); // Intent传值示例 intent.putExtra("NAME", "kevin"); Bundle bundle = new Bundle(); bundle.putInt("AGE", 22); intent.putExtras(bundle); intent.setAction("com.aspook.localbroadcast_AA"); // 异步执行 LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent); // 同步执行 //LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcastSync(intent); Log.d(TAG, "do other things after localbroadcast!!!"); // 在子线程发送本地广播// new Thread(new Runnable() {// @Override// public void run() {// Log.d(TAG, "send broadcast=== thread id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName());//// Intent intent = new Intent();// intent.putExtra("NAME", "kevin");// Bundle bundle = new Bundle();// bundle.putInt("AGE", 22);// intent.putExtras(bundle);// // action should defined as message constant// intent.setAction("com.aspook.localbroadcast_AAA");// // 异步执行// LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);// // 同步执行// //LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcastSync(intent);// Log.d(TAG, "do other things after localbroadcast!!!");// }// }).start(); } }); } @Override protected void onDestroy() { super.onDestroy(); }}
上面代码只是一个本地广播的应用演示,具体使用时可以再进一步抽象封装,可用作应用内的通信机制。
- LocalBroadcastManager原理分析及应用
- Android 本地广播LocalBroadcastManager原理分析
- BroadcastReceiver与LocalBroadcastManager应用及区别
- RemoteViews原理分析及应用
- Android中LocalBroadcastManager的基本用法及源码分析
- 揭秘LocalBroadcastManager实现原理
- LocalBroadcastManager原理和机制
- LocalBroadcastManager源码分析
- LocalBroadcastManager源码分析
- LocalBroadcastManager机制分析
- DirectShow组件原理分析及应用
- DirectShow组件原理分析及应用
- DirectShow组件原理分析及应用
- DirectShow组件原理分析及应用
- DirectShow组件原理分析及应用
- DirectShow组件原理分析及应用
- DirectShow组件原理分析及应用 .
- DirectShow组件原理分析及应用
- C++学习笔记
- 第七周项目三—负数把正数赶出队列
- @RequestMapping 用法详解之地址映射
- JSONP跨域请求数据报错 “Unexpected token :”的解决办法
- 第五周 项目三
- LocalBroadcastManager原理分析及应用
- java并发
- 【云栖大会】云计算是普惠科技 阿里云将与制造业紧密结合推进“中国智造”
- C# 特性(Attribute)
- Unity3D
- Linux 下安装OracleXE 11g
- Android 必知必会
- Android内存泄漏总结
- 高并发Java(7):并发设计模式