Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android編程之LocalBroadcastManager源碼詳解

Android編程之LocalBroadcastManager源碼詳解

編輯:關於Android編程

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.


接下來如正題,先看一下全局變量:

private final Context mAppContext;
private final HashMap> mReceivers = new HashMap();

private final HashMap> mActions = new HashMap();

private final ArrayList mPendingBroadcasts = new ArrayList();
static final int MSG_EXEC_PENDING_BROADCASTS = 1;
private final Handler mHandler;
private static final Object mLock = new Object();
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是一個單例。


看一下它的構造方法,標准的單例寫法:

public static LocalBroadcastManager getInstance(Context context) {
	synchronized (mLock) {
		if (mInstance == null) {
			mInstance = new LocalBroadcastManager(
					context.getApplicationContext());
		}
		return mInstance;
	}
}

private LocalBroadcastManager(Context context) {
	this.mAppContext = context;
	this.mHandler = new Handler(context.getMainLooper()) {
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case MSG_EXEC_PENDING_BROADCASTS:
				LocalBroadcastManager.this.executePendingBroadcasts();
				break;
			default:
				super.handleMessage(msg);
			}
		}
	};
}

上面談到的mAppContext是ApplicationContext的證據:mInstance = new LocalBroadcastManager(context.getApplicationContext());

而mHandler就是在構造時創建的,內部就做了一件事,發送廣播:executePendingBroadcasts。


接下來就看一下如何注冊廣播接收者:

public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
	synchronized (this.mReceivers) {
		ReceiverRecord entry = new ReceiverRecord(filter, receiver);
		ArrayList filters = (ArrayList) this.mReceivers.get(receiver);
		if (filters == null) {
			filters = new ArrayList(1);
			this.mReceivers.put(receiver, filters);
		}
		filters.add(filter);
		for (int i = 0; i < filter.countActions(); i++) {
			String action = filter.getAction(i);
			ArrayList entries = (ArrayList) this.mActions.get(action);
			if (entries == null) {
				entries = new ArrayList(1);
				this.mActions.put(action, entries);
			}
			entries.add(entry);
		}
	}
}

就是將BroadcastReceiver和IntentFilter建立一對多的對應關系。可以通過BroadcastReceiver找到其對應的IntentFilter,也可以通過IntentFilter中的action找到所對應的BroadcastReceiver。這裡還要解釋一下上面提到的mReceivers中,為什麼保存的IntentFilter是數組形式。我們知道,IntentFilter中是可以保存多個action的,這也就是為什麼它初始化成1個長度的數組。那麼這裡的數組的意義,其實很簡單,就是能支持傳入多個IntentFilter。雖然是支持傳入多個IntentFilter,但如果裡面的action是同名的話,也還是按同一個處理的,後面代碼就能看出,action是以鍵的方法存起來的。


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

public void unregisterReceiver(BroadcastReceiver receiver) {
	synchronized (this.mReceivers) {
		ArrayList filters = (ArrayList) this.mReceivers.remove(receiver);
		if (filters == null) {
			return;
		}
		for (int i = 0; i < filters.size(); i++) {
			IntentFilter filter = (IntentFilter) filters.get(i);
			for (int j = 0; j < filter.countActions(); j++) {
				String action = filter.getAction(j);
				ArrayList receivers = (ArrayList) this.mActions.get(action);
				if (receivers != null) {
					for (int k = 0; k < receivers.size(); k++) {
						if (((ReceiverRecord) receivers.get(k)).receiver == receiver) {
							receivers.remove(k);
							k--;
						}
					}
					if (receivers.size() <= 0)
					this.mActions.remove(action);
				}
			}
		}
	}
}

簡單來說,就是將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)的,由於這個方法內容太長,這裡分段來講解:

String action = intent.getAction();
String type = intent.resolveTypeIfNeeded(this.mAppContext.getContentResolver());

Uri data = intent.getData();
String scheme = intent.getScheme();
Set categories = intent.getCategories();

boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION ) != 0;

首先取出intent中的參數,除了action外,其他都是用來作比較的。如果在intent中setFlag設置了Intent.FLAG_DEBUG_LOG_RESOLUTION,就會可以輸出一些log信息。


然後就是從mActions中取出intent的action所對應的ReceiverRecord

ArrayList entries = (ArrayList) this.mActions.get(intent.getAction());
										
ArrayList receivers = null;
for (int i = 0; i < entries.size(); i++)

這段就是for循環裡面的內容:

ReceiverRecord receiver = (ReceiverRecord) entries.get(i);

if (receiver.broadcasting) {
					
	} else {
		int match = receiver.filter.match(action, type, scheme,
		data, categories, "LocalBroadcastManager");

		if (match >= 0) {						
			if (receivers == null) {
				receivers = new ArrayList();
			}
			receivers.add(receiver);
			receiver.broadcasting = true;
		} else {
												
		}
	}
}

在這裡,如果是發送中,就什麼也不做。否則,先匹配一下receiver中的IntentFilter,如果匹配上,就設置其為發送中,即設置變量broadcasting為true。其意義在於避免重復發送。


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

if (receivers != null) {
	for (int i = 0; i < receivers.size(); i++) {
		((ReceiverRecord) receivers.get(i)).broadcasting = false;
	}
	this.mPendingBroadcasts.add(new BroadcastRecord(intent,
			receivers));
	if (!this.mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
		this.mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
	}
	return true;
}

executePendingBroadcasts()方法就很簡單了,就是取出mPendingBroadcasts數組中的BroadcastReceiver(在ReceiverRecord中保存其對象),調用其onReceive方法。

private void executePendingBroadcasts() {
	while (true) {
		BroadcastRecord[] brs = null;
		synchronized (this.mReceivers) {
			int N = this.mPendingBroadcasts.size();
			if (N <= 0) {
				return;
			}
			brs = new BroadcastRecord[N];
			this.mPendingBroadcasts.toArray(brs);
			this.mPendingBroadcasts.clear();
		}
		for (int i = 0; i < brs.length; i++) {
			BroadcastRecord br = brs[i];
			for (int j = 0; j < br.receivers.size(); j++)
				((ReceiverRecord) br.receivers.get(j)).receiver.onReceive(
						this.mAppContext, br.intent);
		}
	}
}

還有一個方法sendBroadcastSync,平常我們一般不會用到這個方法,這裡放一下源碼:

public void sendBroadcastSync(Intent intent) {
	if (sendBroadcast(intent))
		executePendingBroadcasts();
}

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

private static class ReceiverRecord {
	final IntentFilter filter;
	final BroadcastReceiver receiver;
	boolean broadcasting;

	ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) {
		this.filter = _filter;
		this.receiver = _receiver;
	}
}

小結:

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

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved