Handler與Looper方法源碼解析

概述

先看一個Android中的HandlerThread是如何使用Looper的。

public class HandlerThread extends Thread {
@Override
public void run() {
......
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
......
Looper.loop();
......
}

public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
}

在線程的run方法中調用Looper的prepare()方法進行準備工作,準備之後就可以通過Looper.myLooper獲取到當前的線程的Looper了。使用然後調用loop方法進入循環處理。使用的時候非常簡單。接下來分析Looper的實現。

而且可以quit來退出線程,quit方法中獲取當前線程的looper,不為null會調用looper對象quit方法退出。

Looper

prepare方法

先分析一下靜態的方法prepare。

public final class Looper {
······
public static void prepare() {
prepare(true);
}
······
}

對外的靜態方法prepare調用私用的帶有參數的prepare方法。

public final class Looper {
······
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
······
}

參數quitAllowed標識是否允許looper退出。

首先調用sThreadLocal參數獲取是否有Looper。

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

sThreadLocal線程的本地對象,用於保存線程相關的變數。如果從sThreadLocal獲取Looper對象不為null,說明線程已經綁定了Looper,直接拋出異常。如果為從sThreadLocal獲取的Looper對象為null,就創建一個Looper,並設置到sThreadLocal中。

public final class Looper {
······
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
······
}

Looper的構造方法中創建MessageQueue,用於保存消息處理。並保存當前的線程到mThread變數中。

總結一下:

  • Looper.prepare()會調用私有帶有參數的prepare方法

    -在私有的帶有參數的prepare方法中會創建Looper對象,並添加到線程的本地對象中。

    -Looper的構造方法中會創建MessageQueue消息隊列對象。

loop方法

public final class Looper {
.......
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasnt called on this thread.");
}
final MessageQueue queue = me.mQueue;
······
}
.......
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
......
}

loop靜態方法中,首先調用myLooper獲取當前的線程的looper。如果為null,說明線程未綁定Looper,直接拋出異常。如果不為null,獲取Looper中的消息隊列MessageQueue。

public final class Looper {
.......
public static void loop() {
······
for (;;) {
Message msg = queue.next(); // 消息隊列為空,將一直阻塞
if (msg == null) {
return;
}
······
}
·····
}
}

接著是死循環,一直從消息隊列中獲取消息message。當msg為null說明MessageQueue正在退出,這裡就直接從死循環中退出。接著分析。

public final class Looper {
public static void loop() {
for (;;) {
······
try {
msg.target.dispatchMessage(msg);
} finally {
}
······
}
······
}
}

消息對象的target對象實際為Handler對象。也就是調用了Handler的dispatchMessage對象來處理消息。

public final class Looper {
······
public static void loop() {
······
for (;;) {
······
msg.recycleUnchecked();
}
······
}
······
}

最後是調用msg的recycleUnchecked。也是就回收msg,以備下次使用。

總結一下Looper.loop()方法:

-獲取當前線程綁定的Looper的消息隊列。

-死循環中不斷從消息隊列中取出Message來處理。有消息就調用消息的target(Handler對象)的來處理。

-最後回收Message。

quit方法

public final class Looper {
······
public void quit() {
mQueue.quit(false);
}
······
}

Looper的quit方法會調用消息隊列的quit方法。

分段閱讀MessageQueue的quit方法。

public final class MessageQueue {
······
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();
}
······
}
}
······
}

MessageQueue方法中先檢測mQuitAllowed用來判斷是否支持退出,主線程的looper是不支持的。

檢測mQuitting值來判斷MessageQueue是否已經退出了。如果mQuitting為true,說明MessageQueue已經退出,就直接返回了。

如果mQuitting是false,接著執行,設置mQuitting = true,接著safe為true,說明是安全退出,會調用 removeAllFutureMessagesLocked()。我們來分析一下removeAllFutureMessagesLocked()

private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
//比較當前的message的執行的時間是否大於當前的時間。如果是就直接的移除所有的消息。
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);
}
}
}

此方法主要是根據刪除執行的時間點大於當前時間點的消息。我接著分析MessageQueue的quit方法。

void quit(boolean safe) {
······
synchronized (this) {
······
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}

nativeWake(mPtr);
}
}

如果safe為false,不是安全退出,直接調用removeAllMessagesLocked()刪除回收所有的Message。

總結一下Looper的quit方法:

-Looper的quit方法會調用消息隊列MessageQueue的quit方法

-MessageQueue的quit方法中,如果是非安全退出,直接移除所有的消息。如果是安全退出直接移除執行時間點大於當前時間點的Message。

主線程中的Looper

主線程中的消息的管理也是通過Looper來實現的。它與普通線程的Looper不同的是有特殊的方法,但原理基本一致。

public final class Looper {
private static Looper sMainLooper; // guarded by Looper.class
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}

public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
}

-主線程的Looper有單獨的變數sMainLooper保存。

-prepareMainLooper方法用於準備主線的Looper,它是在ActivityThread的main方法中調用的,也就是創建主線程時就會創建了。

-getMainLooper方法獲取主線程的Looper。

Handler

A Handler allows you to send and process Message and Runnable objects associated with a thread』s MessageQueue. Each Handler instance is associated with a single thread and that thread』s message queue.

從描述可以總結出Handler一些特性:

1.每個Handler實例都會與一個線程以及線程的messageQueue關聯。

2.發送消息。

3.處理消息,處理Runnable。

Handler與Looper的關係圖:

Handler處理機制

從上面的特性展開了三個問題:

1.Handler是如何與線程的Looper關聯的?

2.Handler是如何發送消息的,發送到哪裡了?

3.Handler是如何處理消息的?

我們一個一個分析。

Handler與Looper關聯

Handler關聯Looper其實就是在構造方法中。以默認函數為例。

public class Handler {
public Handler() {
this(null, false);
}
/**
* @hide
*/
public Handler(Callback callback, boolean async) {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Cant create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
}

不傳遞任何參數將調用一個其他的構造方法。構造方法中直接獲取Looper.myLooper()來獲取當前線程的Looper,保存到mLooper中,然後獲取mLooper的消息隊列mQueue保存到mQueue中。保存Callback用於處理消息。最後一個是mAsynchronous,標識消息是否為非同步處理。

這樣就關聯到了Looper。

Hanlder處理消息

我們在Looper的loop方法中分析到,處理消息時回調用Message的target的dispatchMessage方法處理,Message的target對象就是Handler對象。

msg.target.dispatchMessage(msg);

那我們來分析dispatchMessage來看看如何處理消息的。

public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}

-如果msg的callback不為null,就是直接調用callback的run方法。一般是通過post方法發送的Runnable。

-然後判斷Handler的mCallback是否為null,不為null就調用mCallback的handleMessage的方法處理。

-最後調用Handler的handleMessage來執行。

Handler發送消息

發送消息通過兩種方式:

-post相關的方法,來發送Runnable。

-sendMessage相關的方法,發送一個Message。

sendMessage方法

我們先來分析sendMessage方法

public class Handler {
······
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
······
}

此方法會調用sendMessageDelayed方法,延遲的時間為0。

public class Handler {
······
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
······
}

此方法又會委託給sendMessageAtTime來發送消息。

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);
}

此方法最後會調用enqueueMessage來完成發送消息。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

設置msg的target對象為當前對象,用於處理msg。

並調用消息隊列MessageQueue的enqueueMessage來添加到隊列中去。

總結:

-sendMessage的方法最終會調用sendMessageAtTime方法,然後調用enqueueMessage()方法,而在enqueueMessage()方法中會設置message.target為此Handler,這樣Looper獲取到的Message可以調用message.target來處理了。

post方法

我們來分析post相關方法。

public class Handler {
······
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
······
}

post方法顯示通過getPostMessage獲取一個Message。

public class Handler {
······
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
······
}

然後通過sendMessageDelayed發送消息。

總結:

-post方法實際上就是封裝成Message再發送。

【附錄】

資料圖

關注+贊同後私信回復【學習】獲取免費學習視頻,學習大綱另外還有像高級UI、性能優化、架構師課程、NDK、混合式開發(ReactNative+Weex)等Android高階開發資料免費分享。

推薦閱讀:

TAG:Android開發 | 源碼閱讀 | Android |