Android编程之LocalBroadcastManager源码详解

http://blog.csdn.net/xyz_fly/article/details/18970569

LocalBroadcastManager 是V4包中的一个类,主要负责程序内部广播的注册与发送。也就是说,它只是适用代码中注册发送广播,对于在AndroidManifest中注册的广播接收,则不适用。

官方英文解释如下:

Helper to register for and send broadcasts of Intents to local objects within your process. This is has a number of advantages over sending global broadcasts with sendBroadcast(Intent):

You know that the data you are broadcasting won't leave your app, so don't need to worry about leaking private data.
It is not possible for other applications to send these broadcasts to your app, so you don't need to worry about having security holes they can exploit.
It is more efficient than sending a global broadcast through the system. 


接下来如正题,先看一下全局变量:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private final Context mAppContext;  
  2. private final HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers = new HashMap();  
  3.   
  4. private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap();  
  5.   
  6. private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList();  
  7. static final int MSG_EXEC_PENDING_BROADCASTS = 1;  
  8. private final Handler mHandler;  
  9. private static final Object mLock = new Object();  
  10. private static LocalBroadcastManager mInstance;  

mAppContext:即ApplicationContext,所以不用担心内存泄漏问题。

mReceivers:记录注册的BroadcastReceiver及其IntentFilter的数组,这里为什么是数组,下面会有讲到。

mActions:记录IntentFilter中的action对应的BroadcastReceiver数组。虽然这里写的是ReceiverRecord类型,但它实际上是一个内部类,主要保存了BroadcastReceiver及其对应的IntentFilter。

mPendingBroadcasts:在发送广播时,会根据Intent的action,找到与之相对应的BroadcastReceiver。还记得吗?action是可以对应多个BroadcastReceiver,所以这里是数组。

mHandler:就做了一件事情,发送广播。

mLock:同步锁。

mInstance:自己本身的实例对象,有经验的可能已经猜到了,没错,LocalBroadcastManager是一个单例。


看一下它的构造方法,标准的单例写法:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public static LocalBroadcastManager getInstance(Context context) {  
  2.     synchronized (mLock) {  
  3.         if (mInstance == null) {  
  4.             mInstance = new LocalBroadcastManager(  
  5.                     context.getApplicationContext());  
  6.         }  
  7.         return mInstance;  
  8.     }  
  9. }  
  10.   
  11. private LocalBroadcastManager(Context context) {  
  12.     this.mAppContext = context;  
  13.     this.mHandler = new Handler(context.getMainLooper()) {  
  14.         public void handleMessage(Message msg) {  
  15.             switch (msg.what) {  
  16.             case MSG_EXEC_PENDING_BROADCASTS:  
  17.                 LocalBroadcastManager.this.executePendingBroadcasts();  
  18.                 break;  
  19.             default:  
  20.                 super.handleMessage(msg);  
  21.             }  
  22.         }  
  23.     };  
  24. }  

上面谈到的mAppContext是ApplicationContext的证据:mInstance = new LocalBroadcastManager(context.getApplicationContext());

而mHandler就是在构造时创建的,内部就做了一件事,发送广播:executePendingBroadcasts。


接下来就看一下如何注册广播接收者:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {  
  2.     synchronized (this.mReceivers) {  
  3.         ReceiverRecord entry = new ReceiverRecord(filter, receiver);  
  4.         ArrayList filters = (ArrayList) this.mReceivers.get(receiver);  
  5.         if (filters == null) {  
  6.             filters = new ArrayList(1);  
  7.             this.mReceivers.put(receiver, filters);  
  8.         }  
  9.         filters.add(filter);  
  10.         for (int i = 0; i < filter.countActions(); i++) {  
  11.             String action = filter.getAction(i);  
  12.             ArrayList entries = (ArrayList) this.mActions.get(action);  
  13.             if (entries == null) {  
  14.                 entries = new ArrayList(1);  
  15.                 this.mActions.put(action, entries);  
  16.             }  
  17.             entries.add(entry);  
  18.         }  
  19.     }  
  20. }  

就是将BroadcastReceiver和IntentFilter建立一对多的对应关系。可以通过BroadcastReceiver找到其对应的IntentFilter,也可以通过IntentFilter中的action找到所对应的BroadcastReceiver。这里还要解释一下上面提到的mReceivers中,为什么保存的IntentFilter是数组形式。我们知道,IntentFilter中是可以保存多个action的,这也就是为什么它初始化成1个长度的数组。那么这里的数组的意义,其实很简单,就是能支持传入多个IntentFilter。虽然是支持传入多个IntentFilter,但如果里面的action是同名的话,也还是按同一个处理的,后面代码就能看出,action是以键的方法存起来的。


有注册,就得有取消注册:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void unregisterReceiver(BroadcastReceiver receiver) {  
  2.     synchronized (this.mReceivers) {  
  3.         ArrayList filters = (ArrayList) this.mReceivers.remove(receiver);  
  4.         if (filters == null) {  
  5.             return;  
  6.         }  
  7.         for (int i = 0; i < filters.size(); i++) {  
  8.             IntentFilter filter = (IntentFilter) filters.get(i);  
  9.             for (int j = 0; j < filter.countActions(); j++) {  
  10.                 String action = filter.getAction(j);  
  11.                 ArrayList receivers = (ArrayList) this.mActions.get(action);  
  12.                 if (receivers != null) {  
  13.                     for (int k = 0; k < receivers.size(); k++) {  
  14.                         if (((ReceiverRecord) receivers.get(k)).receiver == receiver) {  
  15.                             receivers.remove(k);  
  16.                             k--;  
  17.                         }  
  18.                     }  
  19.                     if (receivers.size() <= 0)  
  20.                     this.mActions.remove(action);  
  21.                 }  
  22.             }  
  23.         }  
  24.     }  
  25. }  

简单来说,就是将BroadcastReceiver从mReceivers和mActions中移除掉。由于BroadcastReceiver是mReceivers的键,所以移除掉比较简单。而mActions就稍微复杂一些,需要根据BroadcastReceiver中的IntentFilter数组,从mActions中移除掉。

还记得注册时,同名的action可以对应不同的BroadcastReceiver吗,注意这里的一句话:if (((ReceiverRecord) receivers.get(k)).receiver == receiver),没错,它只会移除掉当前的,不会将action对应的BroadcastReceiver都删除掉。


最后就是关键的    public boolean sendBroadcast(Intent intent)方法,整个方法都是synchronized (this.mReceivers)的,由于这个方法内容太长,这里分段来讲解:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. String action = intent.getAction();  
  2. String type = intent.resolveTypeIfNeeded(this.mAppContext.getContentResolver());  
  3.   
  4. Uri data = intent.getData();  
  5. String scheme = intent.getScheme();  
  6. Set categories = intent.getCategories();  
  7.   
  8. boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION ) != 0;  

首先取出intent中的参数,除了action外,其他都是用来作比较的。如果在intent中setFlag设置了Intent.FLAG_DEBUG_LOG_RESOLUTION,就会可以输出一些log信息。


然后就是从mActions中取出intent的action所对应的ReceiverRecord

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. ArrayList entries = (ArrayList) this.mActions.get(intent.getAction());  
  2.                                           
  3. ArrayList receivers = null;  
  4. for (int i = 0; i < entries.size(); i++)  

这段就是for循环里面的内容:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. ReceiverRecord receiver = (ReceiverRecord) entries.get(i);  
  2.   
  3. if (receiver.broadcasting) {  
  4.                       
  5.     } else {  
  6.         int match = receiver.filter.match(action, type, scheme,  
  7.         data, categories, "LocalBroadcastManager");  
  8.   
  9.         if (match >= 0) {                          
  10.             if (receivers == null) {  
  11.                 receivers = new ArrayList();  
  12.             }  
  13.             receivers.add(receiver);  
  14.             receiver.broadcasting = true;  
  15.         } else {  
  16.                                                   
  17.         }  
  18.     }  
  19. <span style="font-size:12px;">}</span>  

在这里,如果是发送中,就什么也不做。否则,先匹配一下receiver中的IntentFilter,如果匹配上,就设置其为发送中,即设置变量broadcasting为true。其意义在于避免重复发送。


最后,添加到ArrayList中:this.mPendingBroadcasts.add(new BroadcastRecord(intent,receivers));通知Handler,执行executePendingBroadcasts()方法。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. if (receivers != null) {  
  2.     for (int i = 0; i < receivers.size(); i++) {  
  3.         ((ReceiverRecord) receivers.get(i)).broadcasting = false;  
  4.     }  
  5.     this.mPendingBroadcasts.add(new BroadcastRecord(intent,  
  6.             receivers));  
  7.     if (!this.mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {  
  8.         this.mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);  
  9.     }  
  10.     return true;  
  11. }  

executePendingBroadcasts()方法就很简单了,就是取出mPendingBroadcasts数组中的BroadcastReceiver(在ReceiverRecord中保存其对象),调用其onReceive方法。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private void executePendingBroadcasts() {  
  2.     while (true) {  
  3.         BroadcastRecord[] brs = null;  
  4.         synchronized (this.mReceivers) {  
  5.             int N = this.mPendingBroadcasts.size();  
  6.             if (N <= 0) {  
  7.                 return;  
  8.             }  
  9.             brs = new BroadcastRecord[N];  
  10.             this.mPendingBroadcasts.toArray(brs);  
  11.             this.mPendingBroadcasts.clear();  
  12.         }  
  13.         for (int i = 0; i < brs.length; i++) {  
  14.             BroadcastRecord br = brs[i];  
  15.             for (int j = 0; j < br.receivers.size(); j++)  
  16.                 ((ReceiverRecord) br.receivers.get(j)).receiver.onReceive(  
  17.                         this.mAppContext, br.intent);  
  18.         }  
  19.     }  
  20. }  

还有一个方法sendBroadcastSync,平常我们一般不会用到这个方法,这里放一下源码:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void sendBroadcastSync(Intent intent) {  
  2.     if (sendBroadcast(intent))  
  3.         executePendingBroadcasts();  
  4. }  

这里再补一个其内部类ReceiverRecord的源码:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private static class ReceiverRecord {  
  2.     final IntentFilter filter;  
  3.     final BroadcastReceiver receiver;  
  4.     boolean broadcasting;  
  5.   
  6.     ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) {  
  7.         this.filter = _filter;  
  8.         this.receiver = _receiver;  
  9.     }  
  10. }  

小结:

1、LocalBroadcastManager在创建单例传参时,不用纠结context是取activity的还是Application的,它自己会取到tApplicationContext。

2、LocalBroadcastManager只适用于代码间的,因为它就是保存接口BroadcastReceiver的对象,然后直接调用其onReceive方法。

3、LocalBroadcastManager注册广播后,当该其Activity或者Fragment不需要监听时,记得要取消注册,注意一点:注册与取消注册在activity或者fragment的生命周期中要保持一致,例如onResume,onPause。

4、LocalBroadcastManager虽然支持对同一个BroadcastReceiver可以注册多个IntentFilter,但还是应该将所需要的action都放进一个IntentFilter,即只注册一个IntentFilter,这只是我个人的建议。

5、LocalBroadcastManager所发送的广播action,只能与注册到LocalBroadcastManager中BroadcastReceiver产生互动。如果你遇到了通过LocalBroadcastManager发送的广播,对面的BroadcastReceiver没响应,很可能就是这个原因造成的。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值