編輯:關於Android編程
在上一篇文章中我們結合實驗講解了有關使用BroadcastReceiver存在的安全性問題並且給出了相應的解決方案,最後一條的解決方案是采用官方v4包中的LocalBroadcastManager來解決的,官方介紹說這種方式不僅安全而且更高效,今天我們就從源碼的角度來了解一下LocalBroadcastManager,如果你對它非常熟悉,可以跳過本文了(*^__^*) …
分析源碼之前,我們首先回顧一下LocalBroadcastManager的用法:
1、定義廣播接收器,代碼如下:
public class LocalBroadcastReceiver extends BroadcastReceiver { public static final String LOCAL_CUSTOM_ACTION = "com.llew.seetao.customaction"; public LocalBroadcastReceiver() { } @Override public void onReceive(Context context, Intent intent) { Log.e(this.getClass().getSimpleName(), "current time is :" + System.currentTimeMillis()); } }2、注冊廣播接收器,代碼如下:
private void registerBroadcastReceiver() { mBroadcastManager = LocalBroadcastManager.getInstance(MainActivity.this); mLocalReceiver = new LocalBroadcastReceiver(); IntentFilter filter = new IntentFilter(LocalBroadcastReceiver.LOCAL_CUSTOM_ACTION); mBroadcastManager.registerReceiver(mLocalReceiver, filter); }3、發送一個廣播,代碼如下:
public void sendBroadcast(View v) { Intent intent = new Intent(LocalBroadcastReceiver.LOCAL_CUSTOM_ACTION); mBroadcastManager.sendBroadcast(intent); }
分析源碼一般是從使用開始分析,在開始分析之前我們先大致了解一下LocalBroadcastManager中都定義了哪些屬性吧,部分屬性如下所示:
private final Context mAppContext; // 緩存集合 private final HashMap主要定義了代表當前App運行環境的mAppContext屬性,然後定義了三個集合類型的屬性,既然是定義為集合類型肯定是用來裝載數據的,具體裝載什麼類型的數據,看泛型定義的是什麼類型就明確了,了解了相關屬性後我們開始分析源碼。> mReceivers = new HashMap >(); private final HashMap > mActions = new HashMap >(); private final ArrayList mPendingBroadcasts = new ArrayList (); private final Handler mHandler; private static LocalBroadcastManager mInstance;
首先mBroadcastManager的實例化是同過LocalBroadcastManager的全局靜態方法getInstance()來實現的,該方法需要一個Context,我們直接傳遞當前的Context就好了。我們進入該方法查看一下是如何進行實例化的,代碼如下:
public static LocalBroadcastManager getInstance(Context context) { synchronized (mLock) { if (mInstance == null) { mInstance = new LocalBroadcastManager(context.getApplicationContext()); } return mInstance; } }此方法很簡單,通過單例模式來保證只有一個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); } } }; }
在構造方法中把傳遞進來的context賦值給了mAppContext,然後通過context的getMainLooper()方法初始化了mHandler變量(這種方式是基於主線程的Looper進行了初始化,之所以采用此方式來初始化mHandler是因為采用了主線程的消息隊列,有關Handler,Message等相關介紹會在後續文章中做詳細解說)並且重寫了handleMessage()方法,在該方法中只處理類型為MSG_EXEC_PENDING_BROADCASTS的消息,當收到此類型的消息時就去調用executePendingBroadcasts()方法,其他情況下轉交給其父類來處理,這就是初始化的過程。
我們接著看注冊廣播的過程,注冊廣播也很簡單,直接調用LocalBroadcastManager的registerReceiver()方法即可,該方法需要傳遞BroadcastReceiver和IntentFilter實例,在以上步驟2中我們定義了BroadcastReceiver和IntentFilter,那就看一看LocalBroadcastManager的registerReceiver()方法都做了怎樣的操作吧,代碼如下:
public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { synchronized (mReceivers) { // 根據傳遞進來的receiver和filter創建ReceiverRecord實例entry ReceiverRecord entry = new ReceiverRecord(filter, receiver); // 根據receiver從緩存中查找 ArrayListfilters = mReceivers.get(receiver); if (filters == null) { // 如果為空就創建,然後加入緩存中 filters = new ArrayList (1); mReceivers.put(receiver, filters); } // 添加操作 filters.add(filter); // 循環遍歷filter中的action for (int i=0; i entries = mActions.get(action); if (entries == null) { // 如果緩存中不存在就創建,然後加入到緩存中 entries = new ArrayList (1); mActions.put(action, entries); } // 添加操作 entries.add(entry); } } }
代碼中的注釋說的很詳細,那還是大致捋一下流程吧,在該方法中通過synchronize關鍵字對mReceivers進行了加鎖操是為了保證數據的一致性,首先根據傳遞進來的receiver和filter構建了一個ReceiverRecord對象entry,見名字就可以猜測是一個封裝,然後根據receiver從緩存集合mReceivers中查找是否存在對應的filters,如果緩存中不存在filters就新建一個並把該filters存儲在mReceivers中,最後把傳遞進來的filter裝載進filters中。緩存完傳遞進來的receiver和filter後開始循環遍歷filter中包含的action,邏輯如剛剛一樣,先是根據action從緩存中查找entries,如果不存在就新建一個然後把新建的entries裝載進mActions緩存中,最後把最初新建的entry裝載進entries中,這就是注冊廣播接收器的主要流程,核心思想是把傳遞進來的receiver和filter添加進緩存中。
我們看一下如何發送一個廣播,代碼如下:
public boolean sendBroadcast(Intent intent) { synchronized (mReceivers) { // 從intent中獲取相應值 final ContentResolver resolver = mAppContext.getContentResolver(); final String action = intent.getAction(); final String type = intent.resolveTypeIfNeeded(resolver); final Uri data = intent.getData(); final String scheme = intent.getScheme(); final Setcategories = intent.getCategories(); // 根據action從緩存中查找相應值 ArrayList entries = mActions.get(intent.getAction()); if (entries != null) { ArrayList receivers = null; // 緩存中存在對應的action集合entries,開始循環遍歷entries for (int i=0; i = 0) { // 匹配成功 if (receivers == null) { receivers = new ArrayList (); } // 把receiver添加進receivers中 receivers.add(receiver); // 設置receiver.broadcasting值為true receiver.broadcasting = true; } } // 如果receivers不為空表示匹配到了BroadcastReceiver if (receivers != null) { // 循環遍歷匹配到的Receiver,把其broadcasting設置為false for (int i=0; i sendBroadcast()方法很簡單,只接收一個Intent,並且返回一個boolean值(true:廣播發送成功,false:廣播發送失敗)。我們接著往下讀代碼碼,為了保證數據一致性還是給mReceivers進行了加鎖操作,先是獲取到intent中攜帶的值,比如:action,type,data,schema,categories等,然後根據intent中的action從mActions緩存中查找entries實例,接下來是個if語句,如果條件成立就進入條件語句否則直接返回false(false可理解為發送廣播失敗),進入if語句後循環遍歷entries集合拿到集合中的每一個ReceiverRecord對象receiver,如果receiver.braodcasting=true則跳過當前操作繼續下一輪循環,否則調用receiver.filter.match()方法來和intent的攜帶值進行匹配,通過查看文檔可知match()方法返回int類型的值,如果返回值小於0就表示匹配失敗,當match大於等於0時進入if語句內把匹配到的receiver記錄下來保存在receivers集合中並且設置其broadcasting值為true,此論循環完成之後receivers中裝載的都是符合條件的Receiver了,然後又做了一個循環把剛剛符合條件的Receiver的broadcasting屬性全都設置為false,最後把這些值放入到pending集合中,加入集合中之後通過mHandler檢查消息對列中是否包含了消息類型為MSG_EXEC_PENDING_BROADCASTS的消息,如果沒有包含就發送一個消息類型為MSG_EXEC_PENDING_BROADCASTS的消息,消息發送之後返回true,表示廣播發送成功。 通過mHandler發送消息類型為MSG_EXEC_PENDING_BROADCASTS的消息也就是說需要在mHandler的handleMessage()方法中對此類消息做出響應,在mHandler的handleMessage的方法中調用到了executePendingBroadcasts()方法,我們看看這個方法所做的操作吧,代碼如下: private void executePendingBroadcasts() { while (true) { BroadcastRecord[] brs = null; // 加鎖操作 synchronized (mReceivers) { // 這個加鎖的代碼塊就是把mPendingBroadcasts中存儲的值移動到臨時變量brs中,然後清空自己 final int N = mPendingBroadcasts.size(); if (N <= 0) { return; } brs = new BroadcastRecord[N]; mPendingBroadcasts.toArray(brs); mPendingBroadcasts.clear(); } // 循環遍歷brs,最後調用到Receiver的onReceive()方法並把相應值傳遞進去 for (int i=0; i方法executePendingBroadcats()的邏輯很簡單,就是先把緩存中的數據移動到一個臨時數組中並把緩存清空,接著依次循環臨時數組中存在的BroadcastRecord,然後回調Receiver的onReceive()方法,這就最終導致我們注冊的BroadcastReceiver的onReceive()方法得到了調用。注冊廣播和發送廣播的邏輯算是分析完了,注冊廣播接收器的核心邏輯就是把注冊的每一個BroadcastReceiver存放在緩存中;發送廣播的核心邏輯就是根據IntentFilter的match()方法做匹配,把匹配到的廣播接收器添加到一個新的緩存集合中後發送消息,捕獲消息後依次回調BroadcastReceiver的onReceive()方法。既然有注冊和發送操作,那肯定也少不了注銷操作,這時候你肯定會猜想注銷廣播接收器的操作應該是把廣播接收器從緩存中清除吧?恭喜你答對了,代碼如下: public void unregisterReceiver(BroadcastReceiver receiver) { synchronized (mReceivers) { ArrayListfilters = mReceivers.remove(receiver); // 緩存中不存在則直接返回 if (filters == null) { return; } // 循環遍歷緩存,找出相匹配的BroadcastReceiver後從緩存中刪除 for (int i=0; i receivers = mActions.get(action); if (receivers != null) { for (int k=0; k 注銷廣播接收器的核心就是從receivers和mActions緩存中刪除相關值,到這裡有關LocalBroadcastManager的源碼算是分析完了,其核心主要是一下兩點:
借助Handler進行消息傳遞使用IntentFilter的match()功能
前文中提到在應用內發送廣播使用LocalBroadcastManager是官方極力推薦的,它不僅安全更且高效,安全主要是用了Handler的消息機制,使消息只能在應用內發送和接收;高效主要是在緩存中直接遍歷查詢比使用原來的Binder機制進行通信的方式效率要高很多。好了,看到這後你是不是決定不再使用原來方式來發送或接受廣播了?呵呵,反正我是拋棄了原來的使用方式……
從安全的角度深入理解BroadcastReceiver就告一段落了,感謝收看(*^__^*) …
surface,這個單詞的意思是浮在表面的,那麼surfaceview就是浮在表面的view了。如果真的這樣解釋,估計有人要拍磚了。然而,話雖不能這麼說,取這個名兒,多少
package com.example.test_login; import android.app.Ac
本篇文章包含以下內容: XML數據的Dom解析 XML數據的Sax解析&n
Android在4.4就已推出新運行時ART,准備替代用了有些時日的Dalvik。不過當時尚屬測試版,主角仍是Dalvik。 直到今年的Google I/O大會,ART才