第一次在掘金上發表文章,生怕說錯了什麼誤人子弟,更不敢標題黨。
若是文章有誤,歡迎您直接在評論指出。java
Handler 與 Message、MessageQueue、Looper 一塊兒構成了 Android 的消息機制,Android 系統經過大量的消息來與用戶進行交互,View 的繪製、點擊事件、Activity 的生命週期回調等都做爲消息由主線程的 Handler 來處理。android
Handler 在消息機制中的做用是:發送和處理消息。git
Handler 還有另外一個重要的做用,跨線程通訊。最多見的就是子線程請求網絡,而後使用 Handler 將請求到的數據 post 到主線程刷新 UI,大名鼎鼎的 Retrofit 也是這麼作的。網絡
建立 Handler 併發
private Handler handler = new Handler() {
// 重寫 handleMessage 來根據不一樣 what 來處理 Message
// 這個方法在 Handler 建立的線程執行
@Override public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0:
MLog.i(msg.obj);
break;
case 1:
break;
default:
}
}
};複製代碼
建立併發送 Message異步
// 獲取一個 Message
Message message = Message.obtain();
message.what = 0;
message.obj = new Object();
// 使用 Handler 發送 Message
// 消息發送完成後 Handler 的 handleMessage(Message msg) 會處理消息
handler.sendMessage(message);
// 延遲 1s 發送 Message
handler.sendMessageDelayed(message, 1000);
// 發送一個空的 Message
handler.sendEmptyMessage(msg.what);
// 延遲發送一個空的 Message
handler.sendEmptyMessageDelayed(0, 1000);
// 還能夠這樣
// 建立 Message 並綁定 Handler
Message message = handler.obtainMessage();
message.what = 0;
message.obj = new Object();
// 發送 Message
message.sendToTarget();複製代碼
使用 Handler 子線程請求數據,主線程刷新 UIasync
// 1. 在主線程建立 Handler(略)
// 2. 子線程請求數據,主線程刷新 UI
new Thread(new Runnable() {
@Override public void run() {
// 獲取網絡數據
final List<Object> datas = getNetData();
// 方法一:將數據做爲 Message 的 obj 發送出去,在 handleMessage 中刷新 UI
Message msg = Message.obtain();
msg.what = 1;
msg.obj = data;
handler.sendMessage(msg);
// 方法二:直接在 post 中刷新 UI
handler.post(new Runnable() {
@Override public void run() {
// 使用 datas 刷新 UI
// 這個方法也會在 Handler 建立的線程執行
}
});
}
}).start();複製代碼
不得不說,上面使用 Handler 的方法會有內存泄漏的風險ide
Handler 內存泄漏的兩個緣由函數
Java 中非靜態內部類和匿名內部類會持有外部類的引用oop
// 這是一個外部類 Handler 不會持有外部類引用
// 顯然 handleMessage 沒地方寫了
Handler handler = new Handler();
// 重寫 handleMessage 後將獲得一個內部類 Handler,之內 handleMessage 是在外部類中實現的
// 它持有外部類引用,可能會引發內存泄漏
Handler handler = new Handler() {
@Override public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0:
MLog.i(msg.obj);
break;
case 1:
break;
default:
}
}
};
// 這裏 Handler 是一個匿名類,但不是內部類
// Runnable 是一個匿名內部類,持有外部類引用,可能會引發內存泄漏
new Handler().post(new Runnable() {
@Override public void run() {
// ...
}
});複製代碼
Handler 的生命週期比外部類長。
分析
解決方案:
代碼
// Handler 弱引用封裝
public class SafetyHandler<T> extends Handler {
/** * 外部引用, 例如 Activity, Fragment, Dialog, View 等 */
private WeakReference<T> mTargetRef;
public SafetyHandler() {
}
public SafetyHandler(T target) {
this.mTargetRef = new WeakReference<>(target);
}
public T getTarget() {
if (isTargetAlive()) {
return mTargetRef.get();
} else {
removeCallbacksAndMessages(null);
return null;
}
}
public void setTarget(T target) {
this.mTargetRef = new WeakReference<>(target);
}
private boolean isTargetAlive() {
return mTargetRef != null && mTargetRef.get() != null;
}
}
// 在 Fragment 中使用方法
// 想重寫 handleMessage 的話,要建立靜態內部類或者外部類,不然有內存泄漏風險
private static class MyHandler extends SafetyHandler<MyFragment> {
MyHandler(MyFragment fragment) {
super(fragment);
}
@Override public void handleMessage(Message msg) {
super.handleMessage(msg);
if(getTarget() != null) {
MyFragment fragment = getTarget();
switch (msg.what) {
// 操做 fragment
}
}
}
}
// 聲明 Handler
MyHandler handler = new MyHandler(this);
// 使用 Handler
handler.sendMessage() ...
// onDestroy
@Override public void onDestroy() {
super.onDestroy();
handler.removeCallbacksAndMessages(null);
}複製代碼
這部分從 ActivityThread 的 main 方法出發,打通整個消息機制的流程,結合源碼體驗效果更佳。
介紹消息機制的原理前,咱們先來看一下 Handler 與 Message、MessageQueue、Looper 這個四個類的做用
注意:每一個線程只能有一個 Looper 和 一個 MessageQueue,能夠有多個 Handler,每一個 Handler 能夠發送和處理多個 Message。
另外,提到消息機制就不得不說一下 Android 中的主線程(UI 線程)
Android 中的主線程經過 Looper.loop() 進入一個無線循環中,不斷的從一個 MessageQueue 取出消息,處理消息,咱們每觸發一個事件,就會向這個 MessageQueue 中添加一個消息,Looper 取出這個消息,Handler 處理這個消息,正是 Looper.loop() 在驅動着 Android 應用運行下去 ,這也是爲何 Looper.loop 爲何不會阻塞住主線程的緣由(固然前提是在 ActivityThread 的 main 函數 中調用)。
本源碼分析基於 API 25,如下源碼中刪除了一些無關的代碼
一、在主線程的入口,ActivityThread 的 main 方法
public static void main(String[] args) {
// 準備主線程的 Looer
Looper.prepareMainLooper();
// 建立 ActivityThread
ActivityThread thread = new ActivityThread();
thread.attach(false);
// 獲取主線程的 Handler
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
// 對消息隊列進行無線輪詢,處理消息
Looper.loop();
// 一旦跳出循環,拋出異常(Android 不容許跳出主線程的 Looper.loop())
throw new RuntimeException("Main thread loop unexpectedly exited");
}複製代碼
-> Looper.prepareMainLooper()
public static void prepareMainLooper() {
// 準備一個 Looper
prepare(false);
synchronized (Looper.class) {
// main Looper 只能初始化一次,再次初始化會拋出異常
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
// 獲取 main Looper
sMainLooper = myLooper();
}
}複製代碼
-> prepare(false)
// 準備一個 Looper,quitAllowed 是否容許 Looper 中的 MessageQueue 退出
// 默認 prepare() 容許退出,主線程這裏不容許退出
private static void prepare(boolean quitAllowed) {
// 先看下 sThreadLocal 是什麼
// static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
// ThreadLocal:線程本地存儲區,每一個線程都有本地存儲區域,這個區域是每一個線程私有的,不一樣的線程不能之間不能彼此訪問
// 若是 sThreadLocal 中有數據,拋出異常,換句話說 prepare() 這個函數每一個線程只能執行一次
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 建立 Looper 保存到該線程的 ThreadLocal 中
sThreadLocal.set(new Looper(quitAllowed));
}複製代碼
-> new Looper(quitAllowed)
private Looper(boolean quitAllowed) {
// 在 Looper 建立的時候建立一個消息隊列
// quitAllowed:消息隊列是否能夠退出,主線的消息隊列確定不容許退出,因此上面是 prepare(false)
// quitAllowed 爲 false 執行 MessageQueue#quit 退出消息隊列時會出現異常
mQueue = new MessageQueue(quitAllowed);
// 獲取 Looper 存在於哪一個線程
mThread = Thread.currentThread();
}複製代碼
-> sMainLooper = myLooper()
public static @Nullable Looper myLooper() {
// 從 sThreadLocal 中獲取當前線程的 Looper
// 若是當前線程沒有掉用 Looper.prepare 返回 null
return sThreadLocal.get();
}複製代碼
-> sMainThreadHandler = thread.getHandler();
final Handler getHandler() {
// 返回 mH
return mH;
}
// mH 在成員變量的位置 new H()
final H mH = new H();
// H 繼承了 Handler 封裝了一系列關於 Acitivty、Service 以及其餘 Android 相關的操做
private class H extends Handler複製代碼
總結:在主線程的 main 方法中,會建立主線程的 Looper、MessageQueue,而後進入 Looper.loop() 循環中,不斷的取出消息,處理消息,以此來驅動 Android 應用的運行。
二、Handler 的建立,Handler 的全部構造方法都會跳轉到下面兩個之一
public Handler(Callback callback, boolean async) {
// Hanlder 是匿名類、內部類、本地類時,若是沒有聲明爲 static 則會出現內存泄漏的警告
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());
}
}
// 獲取 Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
}
// 消息隊列,從 Looper 中獲取
mQueue = mLooper.mQueue;
// 處理消息的回調接口
mCallback = callback;
// 處理消息的方式是否爲異步,默認同步
mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}複製代碼
總結:在 Handler 的構造方法中,Handler 和 Looper、MessageQueue 綁定起來,若是當前線程沒有 Looper 拋出異常(這也是爲何直接在子線程建立 Handler 會出現異常)。
三、使用 Handler 發送消息
-> sendMessageAtTime(Message msg, long uptimeMillis)
// 除了 sendMessageAtFrontOfQueue,Handler 全部的 post、sendMessage 都會跳到這個方法
// Message msg: 要發送的消息
// long uptimeMillis: 發送消息的絕對時間,經過 SystemClock.uptimeMillis() 加上咱們本身的延遲時間 delayMillis 計算而來
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
// 消息隊列爲空(可能已經退出)返回 false 入隊失敗
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);
}複製代碼
-> sendMessageAtFrontOfQueue(Message msg)
// 發送消息到 MessageQueeu 的隊頭
public final boolean sendMessageAtFrontOfQueue(Message msg) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
// 經過設置 uptimeMillis 爲 0,是消息加入到 MessageQueue 的隊頭
return enqueueMessage(queue, msg, 0);
}複製代碼
-> enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
// 全部 Handler 的 post 、sendMessage 系列方法和 runOnUiThread 最終都會調用這個方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// msg.target 是一個 Handler,將 Message 和 Handler 綁定
// 也就是用哪一個 Handler 發送消息,這個 Message 就和哪一個 Handler 綁定
msg.target = this;
// 若是設置了消息處理方式爲異步處理
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// MessageQueue 的方法,將消息入隊
return queue.enqueueMessage(msg, uptimeMillis);
}複製代碼
-> MessageQueue#enqueueMessage(Message msg, long when)
boolean enqueueMessage(Message msg, long when) {
// Messgae 沒有綁定 Handler 拋出異常
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
// Messgae 正在使用 拋出異常
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
// 消息隊列正在退出,回收 Message
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle(); // 調用 Message#recycleUnchecked()
return false;
}
msg.markInUse(); // 標記 Message 正在使用
msg.when = when; // 設置 Message 的觸發時間
// mMessages 記錄着 MessageQueue 的隊頭的消息
Message p = mMessages;
boolean needWake;
// MessageQueue 沒有消息、Message 觸發時間爲 0、Messgae 觸發時間比隊頭 Message 早
// 總之這個 Message 在 MessageQueue 中須要最早被分發
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p; // 將之前的隊頭 Message 連接在這個 Message 後面
mMessages = msg; // 將這個 Message 賦值給 mMessages
needWake = mBlocked; // 隊列是否阻塞
} else {
// 標記隊列是否阻塞
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
// 按照時間順序將 Message 插入消息隊列
for (;;) {
prev = p; // prev 記錄隊頭
p = p.next; // p 記錄隊頭的後一個
// 隊頭後面沒有消息或者其觸發事件比要插入的 Message 晚,跳出循環
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
// 將 Message 插入隊列
msg.next = p;
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}複製代碼
總結:到如今爲止,咱們的 Handler 已經將 Message 發送到了 MessageQueue,Message 靜靜的等待被處理。
四、Looper.loop() 還記得這個方法在 ActivityThread 的 main 調用了嗎?正是它在不斷處理 MessageQueue 裏面的消息。
public static void loop() {
// 獲取 Looper.Looper.prepare 準備好的 Looper
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 獲取 Looper 中的消息隊列
final MessageQueue queue = me.mQueue;
// 進入無線循環
for (;;) {
// 取出下一條消息
Message msg = queue.next();
// 沒有消息,退出 loop
// 其實上面 queue.next() 也是一個無限循環,獲取到消息就返回,沒有消息就一直循環
if (msg == null) {
return;
}
try {
// msg.target 實際上就是一個 Handler
// 獲取到了消息,使用綁定的 Handler#dispatchMessage 分發消息
msg.target.dispatchMessage(msg);
} finally {
}
// 釋放消息,把 Message 的各個變量清空而後放進消息池中
msg.recycleUnchecked();
}
}複製代碼
五、Handler#dispatchMessage(msg) 消息是如何處理的
public void dispatchMessage(Message msg) {
// 1
if (msg.callback != null) {
handleCallback(msg);
} else {
// 2
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 3. 看到這個方法沒有!就是咱們建立 Handler 時重寫的 handleMessage
// OK 整個流程打通!
handleMessage(msg);
}
}複製代碼
總結:流程雖然通了,可是處理 Message 的方法貌似有三種(我標記了序號),並且咱們的 handleMessage 的優先級最低,其餘方法會在什麼狀況下執行呢? 直接說結論了,調用 Handler 的 post 系列方法會走序號1的處理,建立 Handler 傳入 Callback 會走序號2 的處理。
Handler 機制總結:想使用 Handler 必需要有 Looper,建立 Looper 的時候會建立 MessageQueue,在 Handler 的構造的時候會綁定這個 Looper 和 MessageQueue,Handler 將 Message 發送到 MessageQueue 中,Looper.loop() 會不斷的從 MessageQueue 取出消息再交給這個 Handler 處理。
new Thread(new Runnable() {
@Override public void run() {
new Handler().post(new Runnable() {
@Override public void run() {
MLog.i("Handler in " + Thread.currentThread().getName());
}
});
}
}).start();複製代碼
答案前面提到了是不能,執行上面的代碼會出現 java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() 這個異常,異常提示咱們,不能再沒有調用 Looper.prepare() 的線程中建立 Handler。
簡單修改下代碼就能夠了,給線程準備好 Looper
new Thread(new Runnable() {
@Override public void run() {
// 準備一個 Looper,Looper 建立時對應的 MessageQueue 也會被建立
Looper.prepare();
// 建立 Handler 並 post 一個 Message 到 MessageQueue
new Handler().post(new Runnable() {
@Override public void run() {
MLog.i("Handler in " + Thread.currentThread().getName());
}
});
// Looper 開始不斷的從 MessageQueue 取出消息並再次交給 Handler 執行
// 此時 Lopper 進入到一個無限循環中,後面的代碼都不會被執行
Looper.loop();
}
}).start();複製代碼
// 1. 建立 HandlerThread
handlerThread = new HandlerThread("myHandlerThread") {
// onLooperPrepared 這個方法子線程執行,由線程的 run 方法調用,能夠在裏面直接建立 Handler
@Override protected void onLooperPrepared() {
super.onLooperPrepared();
new Handler().post(new Runnable() {
@Override public void run() {
// 注意:Handler 在子線程建立,這個方法也會運行在子線程,不能夠更新 UI
MLog.i("Handler in " + Thread.currentThread().getName());
}
});
}
};
// 2. 準備 HandlerThread 的 Looper 並調用 onLooperPrepared
handlerThread.start();
// 3. 退出
@Override public void onDestroy() {
super.onDestroy();
handlerThread.quit();
}
// 也能夠這樣用
// 1. 建立 HandlerThread 並準備 Looper
handlerThread = new HandlerThread("myHandlerThread");
handlerThread.start();
// 2. 建立 Handler 並綁定 handlerThread 的 Looper
new Handler(handlerThread.getLooper()).post(new Runnable() {
@Override public void run() {
// 注意:Handler 綁定了子線程的 Looper,這個方法也會運行在子線程,不能夠更新 UI
MLog.i("Handler in " + Thread.currentThread().getName());
}
});
// 3. 退出
@Override public void onDestroy() {
super.onDestroy();
handlerThread.quit();
}複製代碼
HandlerThread 繼承了 Thread,本質是一個擁有 Looper 的線程,所以在 HandlerThread 咱們能夠直接使用 Handler。
構造方法
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
// 傳入線程的名稱和優先級
// 注意 priority 的值必須來自 android.os.Process 不能來自 java.lang.Thread
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}複製代碼
run 方法:建立子線程的 Looper
@Override
public void run() {
mTid = Process.myTid();
// 準備一個 Looper
Looper.prepare();
synchronized (this) {
// 獲取 Looper
mLooper = Looper.myLooper();
// Looper 獲取成功後,喚醒 getLooper 的 wait
notifyAll();
}
Process.setThreadPriority(mPriority);
// Looper 準備好的回調,在這個方法裏可使用 Handler 了
onLooperPrepared();
// Looper 開始循環取消息
Looper.loop();
mTid = -1;
}複製代碼
getLooper 方法:獲取子線程的 Looper
public Looper getLooper() {
// 線程沒有開始或者死亡,返回 null
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
// Looper 的建立時在子線程完成的,而 getLooper 可能會在主線程調用
// 當 Looper 沒有建立完成時,使用 wait 阻塞等待
// 上面在 Looper 建立好後會 notifyAll 來喚醒 wait
synchronized(this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}複製代碼
quit 和 quitSafely :結束 Looper 的運行
// quit
quit() -> looper.quit() -> mQueue.quit(false);
// quitSafely
quitSafely() -> looper.quitSafely() -> mQueue.quit(true);
// 這兩個方法最終都會調用到 MessageQueue 的 void quit(boolean safe) 方法
// 前者會直接移除 MessageQueue 中的全部消息,而後終止 MessageQueue
// 後者會將 MessageQueue 中已有消息處理完成後(再也不接收新消息)終止 MessageQueue複製代碼