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 plaincopy在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 plaincopy在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 plaincopy在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 plaincopy在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 plaincopy在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 plaincopy在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 plaincopy在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 plaincopy在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 plaincopy在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 plaincopy在CODE上查看代碼片派生到我的代碼片
  1. public void sendBroadcastSync(Intent intent) {  
  2.     if (sendBroadcast(intent))  
  3.         executePendingBroadcasts();  
  4. }  

這裏再補一個其內部類ReceiverRecord的源碼:

[java] view plaincopy在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沒響應,很可能就是這個原因造成的。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章