[TOC]
一句話總結:Looper 不斷從 MessageQueue 中取出一個 Message,然后交給其對應的 Handler 處理。<!--more-->
先上一幅整體關系圖,來自郭神

# Handler使用
Handler在Android中主要用于線程間的通信,經典使用方法如下:
```java
Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
//...
}
}
};
Message message = Message.obtain();
message.what = 1;
mHandler.sendMessage(message);
```
下面我們通過源碼來一起看看Handler的真面貌。
# Handler源碼分析
Handler類在整個機制中所做的事情只有兩件,一個是發布新消息,一個是處理消息,我們分別來看看。
## Handler發布消息
Handler的sendMessage()方法源碼如下:
```java
public final boolean sendMessage(Message msg){
// 默認延遲時間為0ms
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 設置Message的target為Handler自己,target是Message類的一個成員變量,為Handler類型的
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
```
代碼并不復雜,最終調用了MessageQueue的enqueueMessage方法將方法發送出去。enqueueMessage源碼如下:
```java
boolean enqueueMessage(Message msg, long when) {
// 檢查Message的target不能為空,因為后面Looper取出消息后,交由這個target(Handler)處理的
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
// 將Message置于消息鏈表的表頭位置
msg.next = p;
mMessages = msg;
} else {
Message prev;
// 死循環遍歷Message鏈表
for (;;) {
prev = p;
p = p.next;
// 如果當前Message的延遲執行時間比所對比的Message執行時間小,就插到它前面
// 不然繼續循環,和多對比Message的next所指向的Message繼續對比
if (p == null || when < p.when) {
break;
}
}
// 將當前Message插入消息鏈表
msg.next = p;
prev.next = msg;
}
}
return true;
}
```

Message插入邏輯在注釋中寫的也很清楚了,注意兩點:
* MessageQueue采用單鏈表結構,表頭的Message是會最先被取出執行的
* 通過死循環遍歷鏈表,對比執行延遲時間,來決定將Message插入到何處
消息已經插入到消息隊列中了,下面我們看看消息又是怎么取出來的。
## Handler處理消息
Handler除了插入消息到消息隊列中,還承擔著處理消息的職責。先從我們重寫的handleMessage方法作為入口。
```java
public void handleMessage(Message msg) {
}
```
handleMessage是Handler類的一個空方法,需要子類來實現,我們看看這個方法是在哪里被調用的。
```java
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
```
dispatchMessage方法是由Looper調用的,Looper在整個Handler機制中的作用是從MessageQueue中不斷取出Message,交由Handler處理,Handler調用dispatchMessage方法進行Message的處理,Looper的源碼我們稍后分析。
Handler在處理消息時分為三種情況:
```java
if (msg.callback != null) {
handleCallback(msg);
}
private static void handleCallback(Message message) {
message.callback.run();
}
```
1、先判斷Message的callback是否為空,不為空直接調用其run方法,不再往下走
2、判斷Handler的mCallback是否為空,不為空直接調用其handleMessage方法,根據返回值決定是否往下走
3、如果Message和Handler的Callback均為空,調用Handler的handleMessage方法
其中,Message的callback在其obtain時進行設置,我們比較少手動調用帶參數的obtain方法,暫不關心;Handler的Callback則是在Handler構造的時候傳入,我們可以手動傳入;Handler的handleMessage方法是我們手動重寫的,用于處理Message。因此,我們可以通過在構造Handler時傳入Callback來處理消息,或者直接重寫Handler的handleMessage方法來處理消息。
補充剛剛Handler構造Callback的源碼:
```java
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Callback callback, boolean async) {
//...
mCallback = callback;
mAsynchronous = async;
}
```
剛剛提到Looper不斷從MessageQueue中提取消息,然后給Handler進行處理,下面我們來看看Looper:
## Looper提取消息
### Looper的創建
Handler類有一個成員變量mLooper:
```java
final Looper mLooper;
```
mLooper的初始化有兩種方式,分別如下:
```java
/**
* @param looper: The looper, must not be null
*/
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public Handler(Callback callback, boolean async) {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
```
可以看到,mLooper一定不能為null。來看看Looper的myLooper()方法,看看如何從當前線程獲取Looper:
```java
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
```
此處引出了ThreadLocal的概念,稍后會詳細分析。暫時可以理解為,Looper對象是當前線程的一個局部變量,其他線程無法訪問及修改, 上面是獲取Looper的方法,下面看看為當前線程設置Looper的方法,操作是在 Looper的prepare()方法完成的,看源碼:
```java
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));
}
```
綜上可以看到,Looper對象是和線程綁定的,一個線程有且只能有一個Looper對象。使用Handler機制來做線程間通信時,要保證該線程的Looper不為空,也就是當我們為一個子線程開啟Handler機制時,需要先調用Looper.prepare()方法來創建Looper對象,后面Looper才可以從消息隊列中取出Message。
下面來看看Looper的構造方法:
```java
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
```
可以看到,Looper有兩個成員變量,一個是消息隊列,一個是當前線程。Looper是如何從消息隊列取消息的呢, 來看看:
```java
public static void loop() {
// 從ThreadLocal中取出Looper
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;
for (;;) {
Message msg = queue.next();
if (msg == null) {
// 沒有消息表明消息隊列正在退出
return;
}
//...
try {
msg.target.dispatchMessage(msg);
}
//...
}
}
// 前面我們在Looper.prepare()方法中,在ThreadLocal中存入新創建的Looper對象。現在取出使用
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
```
邏輯很清晰,先從ThreadLocal中取出我們創建的Looper對象,從Looper中拿到消息隊列。然后一個for死循環,從消息隊列中循環取出消息,調用msg.target的dispatchMessage方法,msg.target就是Handler對象,也就是將消息交由Handler處理。
### 拓展:ThreadLocal
1、ThreadLocal是一個創建線程局部變量的類。
2、一般情況下,我們創建的變量可以被任何一個線程訪問并修改,而使用ThreadLocal創建的變量只能被當前線程訪問,其他線程無法訪問和修改。
ThreadLocal使用方法:
```java
// 創建
ThreadLocal<String> mStringThreadLocal = new ThreadLocal<>();
// set方法
mStringThreadLocal.set("Canton");
// get方法
mStringThreadLocal.get();
// 設置默認初始值
ThreadLocal<String> mNewStringThreadLocal = new ThreadLocal<String>() {
@Override
protected String initialValue() {
return "A new threadLocal";
}
}
```
ThreadLocal原理:
先從ThreadLocal的set方法看看ThreadLocal是如何存儲的:
```java
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
// Thread類的threadLocals聲明如下:
// ThreadLocal.ThreadLocalMap threadLocals = null;
// 是一個ThreadLocalMap
return t.threadLocals;
}
// 一個自定義的HashMap,用來存放線程局部變量值
static class ThreadLocalMap {
//...
}
// 創建存放線程局部變量值的HashMap
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
```
ThreadLocal的get方法源碼如下:
```java
public T get() {
// 獲取當前線程
Thread t = Thread.currentThread();
// 獲取當前線程的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
T result = (T)e.value;
return result;
}
return setInitialValue();
}
```
可以看到
a、ThreadLocal的set方法存入的值,實際是放到當前線程的ThreadLocalMap中,key為當前ThreadLocal對象,value為存入的對象。
b、get方法直接從當前線程的ThreadLocalMap中,根據key獲取對象。
c、注意,ThreadLocal的值并不是存放在ThreadLocal中的!
## 發送消息的其他方法
除了使用Handler的sendMessage方法,還有其他幾種方法可以發送Message到消息隊列中,總結如下:
1、使用Handler的post方法發送消息:
```java
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
```
可以看到,和Handler的sendMessage方法一樣,還是調用了Handler的sendMessageDelayed方法。區別在于,調用了getPostMessage方法為Message設置了Callback。前面處理消息時,我們分析過,如果Message有設置Callback時,直接回調Message的Callback的run方法,不再執行Handler的Callback或者Handler的handleMessage方法,這點需要注意。
2、使用View的post方法發送消息:
```java
public boolean post(Runnable action) {
// 當一個View添加到Window時,會在AttatchInfo中存儲一些信息,其中就有UI線程的Handler
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
// 調用Handler的post方法
return attachInfo.mHandler.post(action);
}
// 如果View還沒被添加到一個Window中,就先將Runnable存到一個隊列中,等View添加到Window并且有Handler了再進行執行
getRunQueue().post(action);
return true;
}
```
3、使用Activity的runOnUiThread方法發送消息
```java
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
// 當前線程不是UI線程,調用UI線程的Handler將Runnable包裝成Message發送出去
mHandler.post(action);
} else {
// 當前線程就是UI線程,直接執行即可
action.run();
}
}
```
## 創建Handler的幾種重載
```java
// 調用兩個參數的構造方法
public Handler() {
this(null, false);
}
// 調用兩個參數的構造方法
public Handler(Callback callback) {
this(callback, false);
}
// 調用兩個參數的構造方法
public Handler(boolean async) {
this(null, async);
}
// 調用三個參數的構造方法
public Handler(Looper looper) {
this(looper, null, false);
}
// 調用三個參數的構造方法
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
```
兩個參數和三個參數的構造方法分別如下:
```java
// 兩個參數的構造方法
public Handler(Callback callback, boolean async) {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
// 三個參數的構造方法
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
```
可以看到,Handler的構造方法重載雖然多,但最終要么調用了兩個參數的構造方法,要么調用了三個參數的構造方法。其中,三個參數的構造方法直接設置了Handler的Looper、Callback、消息是同步還是異步;兩個參數的少了一個Looper參數,直接調用了Looper.myLooper方法,來從ThreadLocal中獲取當前線程的Looper。
因此,在UI線程調用兩個參數的構造方法時,會自動獲取UI線程的Looper,并設置給Handler;但是直接在子線程調用兩個參數的構造方法時,由于還沒調用Looper.prepare(),因此子線程的Looper為空,所以會直接拋出異常。
注意!
Android 中除了 UI 線程(主線程),創建的工作線程默認是沒有消息循環和消息隊列的。如果想讓該線程具有消息隊列和消息循環,并具有消息處理機制,就需要在線程中首先調用 Looper.prepare()來創建消息隊列,然后調用 Looper.loop()開啟消息循環。
## HandlerThread
關于 HandlerThread 的使用,源碼中是這樣說的,使用 HandlerThread 可以很方便的開啟一個包含 Looper 的線程,開啟線程后,可以使用和該線程綁定的 Looper 去構建相應的 Handler。并且可以通過 HandlerThread 的 quit 和 quitSafely 方法很方便的終止線程的消息循環隊列。
HandlerThread 使用示例:
```java
HandlerThread handlerThread = new HandlerThread("test");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//...
}
};
handlerThread.quit();
handlerThread.quitSafely();
```
其中HandlerThread的源碼大概如下:
```java
public class HandlerThread extends Thread {
@Override
public void run() {
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
}
onLooperPrepared();
Looper.loop();
//...
}
}
public Looper getLooper() {
//...
return mLooper;
}
```
可以看到,HandlerThread繼承自Thread,在run方法中,調用Looper的prepare方法創建Looper對象并和HandlerThread進行綁定,最后調用Looper的loop方法開啟循環提取消息。我們在創建Handler時,直接將和HandleThread綁定在一起的Looper傳遞給Handler即可。這樣子Handler就會分發以及處理HandlerThread上的消息。
# 整體流程總結
1、Handler調用sendMessage、post方法發送Message,并插入到MessageQueue中,MessaQueue采用單鏈表結構。
2、Handler類有一個Looper成員變量,Looper屬于線程局部變量,每個線程有且只能有一個Looper;Looper類有一個MessageQueue成員變量;Handler持有Looper主要是為了拿到Looper對應的MessageQueue,并往其中插入消息。
3、子線程需要先調用Looper.prepare方法,來創建一個Looper對象存儲到ThreadLocal中。然后創建Handler時會調用Looper.myLooper方法獲取當前線程的Looper。
4、Looper.loop方法開啟消息循環,Looper會循環從其MessageQueue中提取消息,并調用消息的target(也就是Handler)進行分發處理
5、Handler拿到Message后,先判斷Message的Callback是否為空,不為空直接執行,消息處理結束;為空則判斷Handler的Callback是否為空,不為空則執行,并決定是否進行攔截,攔截則消息處理結束;不攔截則執行Handler的handleMessage方法。
參考文檔:
[深入理解 Android 內核設計思想](https://book.douban.com/subject/25921329/)
[Android消息機制(一):概述設計架構](http://www.jianshu.com/p/8656bebc27cb)
[Android消息隊列模型](http://superonion.iteye.com/blog/1442416)
[Android 多線程編程的總結](https://www.diycode.cc/topics/213)
- 導讀
- Java知識
- Java基本程序設計結構
- 【基礎知識】Java基礎
- 【源碼分析】Okio
- 【源碼分析】深入理解i++和++i
- 【專題分析】JVM與GC
- 【面試清單】Java基本程序設計結構
- 對象與類
- 【基礎知識】對象與類
- 【專題分析】Java類加載過程
- 【面試清單】對象與類
- 泛型
- 【基礎知識】泛型
- 【面試清單】泛型
- 集合
- 【基礎知識】集合
- 【源碼分析】SparseArray
- 【面試清單】集合
- 多線程
- 【基礎知識】多線程
- 【源碼分析】ThreadPoolExecutor源碼分析
- 【專題分析】volatile關鍵字
- 【面試清單】多線程
- Java新特性
- 【專題分析】Lambda表達式
- 【專題分析】注解
- 【面試清單】Java新特性
- Effective Java筆記
- Android知識
- Activity
- 【基礎知識】Activity
- 【專題分析】運行時權限
- 【專題分析】使用Intent打開三方應用
- 【源碼分析】Activity的工作過程
- 【面試清單】Activity
- 架構組件
- 【專題分析】MVC、MVP與MVVM
- 【專題分析】數據綁定
- 【面試清單】架構組件
- 界面
- 【專題分析】自定義View
- 【專題分析】ImageView的ScaleType屬性
- 【專題分析】ConstraintLayout 使用
- 【專題分析】搞懂點九圖
- 【專題分析】Adapter
- 【源碼分析】LayoutInflater
- 【源碼分析】ViewStub
- 【源碼分析】View三大流程
- 【源碼分析】觸摸事件分發機制
- 【源碼分析】按鍵事件分發機制
- 【源碼分析】Android窗口機制
- 【面試清單】界面
- 動畫和過渡
- 【基礎知識】動畫和過渡
- 【面試清單】動畫和過渡
- 圖片和圖形
- 【專題分析】圖片加載
- 【面試清單】圖片和圖形
- 后臺任務
- 應用數據和文件
- 基于網絡的內容
- 多線程與多進程
- 【基礎知識】多線程與多進程
- 【源碼分析】Handler
- 【源碼分析】AsyncTask
- 【專題分析】Service
- 【源碼分析】Parcelable
- 【專題分析】Binder
- 【源碼分析】Messenger
- 【面試清單】多線程與多進程
- 應用優化
- 【專題分析】布局優化
- 【專題分析】繪制優化
- 【專題分析】內存優化
- 【專題分析】啟動優化
- 【專題分析】電池優化
- 【專題分析】包大小優化
- 【面試清單】應用優化
- Android新特性
- 【專題分析】狀態欄、ActionBar和導航欄
- 【專題分析】應用圖標、通知欄適配
- 【專題分析】Android新版本重要變更
- 【專題分析】唯一標識符的最佳做法
- 開源庫源碼分析
- 【源碼分析】BaseRecyclerViewAdapterHelper
- 【源碼分析】ButterKnife
- 【源碼分析】Dagger2
- 【源碼分析】EventBus3(一)
- 【源碼分析】EventBus3(二)
- 【源碼分析】Glide
- 【源碼分析】OkHttp
- 【源碼分析】Retrofit
- 其他知識
- Flutter
- 原生開發與跨平臺開發
- 整體歸納
- 狀態及狀態管理
- 零碎知識點
- 添加Flutter到現有應用
- Git知識
- Git命令
- .gitignore文件
- 設計模式
- 創建型模式
- 結構型模式
- 行為型模式
- RxJava
- 基礎
- Linux知識
- 環境變量
- Linux命令
- ADB命令
- 算法
- 常見數據結構及實現
- 數組
- 排序算法
- 鏈表
- 二叉樹
- 棧和隊列
- 算法時間復雜度
- 常見算法思想
- 其他技術
- 正則表達式
- 編碼格式
- HTTP與HTTPS
- 【面試清單】其他知識
- 開發歸納
- Android零碎問題
- 其他零碎問題
- 開發思路