歡迎訪問我的個(gè)人博客 傳送門
成都網(wǎng)站建設(shè)哪家好,找成都創(chuàng)新互聯(lián)公司!專注于網(wǎng)頁設(shè)計(jì)、成都網(wǎng)站建設(shè)、微信開發(fā)、微信小程序開發(fā)、集團(tuán)成都企業(yè)網(wǎng)站定制等服務(wù)項(xiàng)目。核心團(tuán)隊(duì)均擁有互聯(lián)網(wǎng)行業(yè)多年經(jīng)驗(yàn),服務(wù)眾多知名企業(yè)客戶;涵蓋的客戶類型包括:成都橡塑保溫等眾多領(lǐng)域,積累了大量豐富的經(jīng)驗(yàn),同時(shí)也獲得了客戶的一致稱譽(yù)!
做過 Android 開發(fā)的童鞋都知道,不能在非主線程修改 UI 控件,因?yàn)?Android 規(guī)定只能在主線程中訪問 UI ,如果在子線程中訪問 UI ,那么程序就會(huì)拋出異常
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy .
并且,Android 也不建議在 UI 線程既主線程中做一些耗時(shí)操作,否則會(huì)導(dǎo)致程序 ANR 。如果我們需要做一些耗時(shí)的操作并且操作結(jié)束后要修改 UI ,那么就需要用到 Android 提供的 Handler 切換到主線程來訪問 UI 。因此,系統(tǒng)之所以提供 Handler,主要原因就是為了解決在子線程中無法訪問 UI 的問題。
要理解 Handler 消息機(jī)制原理 還需要了解幾個(gè)概念:
UI 線程
主線程 ActivityThread
Message
Handler 發(fā)送和處理的消息,由 MessageQueue 管理。
MessageQueue
消息隊(duì)列,用來存放通過 Handler 發(fā)送的消息,按照先進(jìn)先出執(zhí)行,內(nèi)部使用的是單鏈表的結(jié)構(gòu)。
Handler
負(fù)責(zé)發(fā)送消息和處理消息。
Looper
負(fù)責(zé)消息循環(huán),循環(huán)取出 MessageQueue 里面的 Message,并交給相應(yīng)的 Handler 進(jìn)行處理。
在應(yīng)用啟動(dòng)時(shí),會(huì)開啟一個(gè) UI 線程,并且啟動(dòng)消息循環(huán),應(yīng)用不停地從該消息列表中取出、處理消息達(dá)到程序運(yùn)行的效果。
Looper 負(fù)責(zé)的就是創(chuàng)建一個(gè) MessageQueue,然后進(jìn)入一個(gè)無限循環(huán)體不斷從該 MessageQueue 中讀取消息,而消息的創(chuàng)建者就是一個(gè)或多個(gè) Handler 。
流程圖如下:
下面結(jié)合源碼來具體分析
Looper 比較重要的兩個(gè)方法是 prepare( ) 和 loop( )
先看下構(gòu)造方法
final MessageQueue mQueue;
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper 在創(chuàng)建時(shí)會(huì)新建一個(gè) MessageQueue
通過 prepare 方法可以為 Handler 創(chuàng)建一個(gè) Lopper,源碼如下:
static final ThreadLocal sThreadLocal = new ThreadLocal();
private static Looper sMainLooper; // guarded by Looper.class
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
//一個(gè)線程只能有一個(gè)looper
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
可以看到這里創(chuàng)建的 Looper 對(duì)象使用 ThreadLocal 保存,這里簡(jiǎn)單介紹下 ThreadLocal。
ThreadLocal 是一個(gè)線程內(nèi)部的數(shù)據(jù)存儲(chǔ)類,通過它可以在指定的線程中存儲(chǔ)數(shù)據(jù),數(shù)據(jù)存儲(chǔ)以后,只有在指定線程中可以獲取到存儲(chǔ)的數(shù)據(jù),對(duì)于其他線程來說則無法獲取到數(shù)據(jù),這樣就保證了一個(gè)線程對(duì)應(yīng)了一個(gè) Looper,從源碼中也可以看出一個(gè)線程也只能有一個(gè) Looper
,否則就會(huì)拋出異常。
prepareMainLooper() 方法是 系統(tǒng)在 ActivityThread 中調(diào)用的。
ActivityThread.java
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
//...省略代碼
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
由此可以看出,系統(tǒng)在創(chuàng)建時(shí),會(huì)自動(dòng)創(chuàng)建 Looper 來處理消息,所以我們一般在主線程中使用 Handler 時(shí),是不需要手動(dòng)調(diào)用 Looper.prepare() 的。這樣 Handler 就默認(rèn)和主線程的 Looper 綁定。
當(dāng) Handler 綁定的 Looper 是主線程的 Looper 時(shí),則該 Handler 可以在其 handleMessage 中更新UI,否則更新 UI 則會(huì)拋出異常。
在開發(fā)中,我們可能在多個(gè)地方使用 Handler,所以又可以得出一個(gè)結(jié)論:一個(gè) Looper 可以和多個(gè) Handler 綁定
,那么 Looper 是怎么區(qū)分 Message 由哪個(gè) Handler 處理呢?
繼續(xù)看源碼 loop()
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (slowDispatchThresholdMs > 0) {
final long time = end - start;
if (time > slowDispatchThresholdMs) {
Slog.w(TAG, "Dispatch took " + time + "ms on "
+ Thread.currentThread().getName() + ", h=" +
msg.target + " cb=" + msg.callback + " msg=" + msg.what);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();// 回收消息
}
}
代碼比較多,我們撿重點(diǎn)看。
2~6 行 獲取當(dāng)前 Looper 如果沒有則拋異常,有則獲取消息隊(duì)列 MessageQueue
所以如果我們?cè)谧泳€程中使用 Handler 則必須手動(dòng)調(diào)用 Looper.prepare() 和 Looper.loop()
系統(tǒng)在代碼中提供了示例代碼
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
獲取到 MessageQueue 后開啟消息循環(huán),不斷從 MessageQueue 中取消息,無則阻塞,等待消息。有則調(diào)用 msg.target.dispatchMessage(msg) 處理消息。
msg.target 就是 Message 所屬的 Handler,這個(gè)會(huì)再后面具體介紹 Handler 中會(huì)說明
所以上面的問題就可以回答了,Looper 不需要考慮怎么區(qū)分 Message 由哪個(gè) Handler 處理,只負(fù)責(zé)開啟消息循環(huán)接收消息并處理消息即可。處理完消息后會(huì)調(diào)用 msg.recycleUnchecked() 來回收消息。
那么開啟消息循環(huán)后,可以停止嗎?
答案是肯定的,Looper 提供了 quit() 和 quitSafely() 來退出。
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
可以看到實(shí)際上調(diào)用的是 MessageQueue 中的退出方法,具體會(huì)在 MessageQueue 中介紹。
調(diào)用 quit() 會(huì)直接退出 Looper,而 quitSafely() 只是設(shè)定一個(gè)退出標(biāo)記,然后把消息隊(duì)列中的已有消息處理完畢后才安全地退出。在 Loooper 退出后,通過 Handler 發(fā)送消息會(huì)失敗。如果在子線程中手動(dòng)創(chuàng)建了 Looper ,則應(yīng)在處理完操作后退出 Looper 終止消息循環(huán)。
到此 Looper 的源碼分析就完了,我們來總結(jié)下 Looper 所做的工作:
Message 是線程通信中傳遞的消息,它有幾個(gè)關(guān)鍵點(diǎn)
MessageQueue
MessageQueue 負(fù)責(zé)管理消息隊(duì)列,通過一個(gè)單鏈表的數(shù)據(jù)結(jié)構(gòu)來維護(hù)。
源碼中有三個(gè)主要方法:
next 方法
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
//...省略代碼
}
}
可以發(fā)現(xiàn) next 方法是一個(gè)無限循環(huán)的方法,如果消息隊(duì)列中沒有消息,那么 next 方法會(huì)一直阻塞在這里。當(dāng)有新消息到來時(shí),next 方法會(huì)從中獲取消息出來返回給 Looper 去處理,并將其從消息列表中移除。
quit方法
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();//移除尚未處理的消息
} else {
removeAllMessagesLocked();//移除所有消息
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
if (p.when > now) {
removeAllMessagesLocked(); // 移除尚未處理的消息
} else { // 正在處理的消息不做處理
Message n;
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
從 上述代碼中可以看出,當(dāng) safe 為 true 時(shí),只移除尚未觸發(fā)的所有消息,對(duì)于正在處理的消息不做處理,當(dāng) safe 為 false 時(shí),移除所有消息。
Handler 是我們使用最多的類,主要用來發(fā)送消息和處理消息。
先來看構(gòu)造方法
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
//獲取當(dāng)前線程的 Looper 實(shí)例,如果不存在則拋出異常
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//關(guān)聯(lián) Looper 中的 MessageQueue 消息隊(duì)列
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
構(gòu)造方法主要有三個(gè)參數(shù)
從源碼中可看出,因?yàn)?UI 線程在啟動(dòng)時(shí)會(huì)自動(dòng)創(chuàng)建 Looper 實(shí)例,所以一般我們?cè)?UI 線程中使用 Handler 時(shí)不需要傳遞 Looper 對(duì)象。而在子線程中則必須手動(dòng)調(diào)用 Looper.prepare 和 Looper.loop 方法,并傳遞給 Handler ,否則無法使用,這一點(diǎn)肯定有不少童鞋都遇到過。
在拿到 Looper 對(duì)象后,Handler 會(huì)獲取 Looper 中的 MessageQueue 消息隊(duì)列,這樣就和 MessageQueue 關(guān)聯(lián)上了。
關(guān)聯(lián)上 MessageQueue ,接下來那我們就看下 Handler 是如何發(fā)送消息的。
Handler 發(fā)送消息方法很多,實(shí)際上最后都是調(diào)用的 enqueueMessage 方法,看圖說話
主要看 enqueueMessage 方法
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到在發(fā)送消息時(shí)給 Message 設(shè)置了 target = this 也就是當(dāng)前的 Handler 對(duì)象,并調(diào)用了 MessageQueue 的 enqueueMessage 方法,這樣就把消息存在消息隊(duì)列,然后由 Looper 處理了。
童鞋們應(yīng)該記得之前在講 Looper 時(shí),說到 Looper 開啟消息循環(huán)后,會(huì)不斷從 MessageQueue 中取出Message,并調(diào)用 msg.target.dispatchMessage(msg) 來處理消息。
接下來,就來看看 Handler 是如何接收消息的也就是 dispatchMessage 方法
public interface Callback {
public boolean handleMessage(Message msg);
}
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
可以看出 Handler 接收消息,只是調(diào)用一個(gè)空方法 handleMessage 是不是有些眼熟呢,看下我們寫過很多次的 Handler 代碼
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case value:
break;
default:
break;
}
}
};
沒錯(cuò)這就是我們自己創(chuàng)建 Handler 時(shí)重寫的方法,由我們來處理消息,然后根據(jù) msg.what 標(biāo)識(shí)進(jìn)行消息處理。
應(yīng)用啟動(dòng)時(shí)會(huì)啟動(dòng) UI 線程也就是主線程 ActivityThread,在 ActivityThread 的 main 方法中會(huì)調(diào)用 Looper.prepareMainLooper( ) 和 Looper.loop ( ) 啟動(dòng) Looper 。
Looper 啟動(dòng)時(shí)會(huì)創(chuàng)建一個(gè) MessageQueue 實(shí)例,并且只有一個(gè)實(shí)例,然后不斷從 MessageQueue 中獲取消息,無則阻塞等待消息,有則調(diào)用 msg.target.dispatchMessage(msg) 處理消息。
我們?cè)谑褂?Handler 時(shí) 需要先創(chuàng)建 Handler 實(shí)例,Handler 在創(chuàng)建時(shí)會(huì)獲取當(dāng)前線程關(guān)聯(lián)的 Looper 實(shí)例 ,和 Looper 中的消息隊(duì)列 MessageQueue。然后在發(fā)送消息時(shí)會(huì)自動(dòng)給 Message 設(shè)置 target 為 Handler 本身,并把消息放入 MessageQueue 中,由 Looper 處理。Handler 在創(chuàng)建時(shí)會(huì)重寫的
handleMessage 方法中處理消息。
如果要在子線程中使用 Handler 就需要新建 Looper 實(shí)例,傳給 Handler 即可。
再看下流程圖
因篇幅較長(zhǎng),童鞋們的 Handler 肯定用的爐火純青了,所以最后就不寫例子了。
本人寫博客不久,寫的不好的地方,望各位海涵。