Android Studio 2.3 API 25 從源碼角度分析Handler機制。有利於使用Handler和分析Handler的相關問題。 Handler 簡介 一個Handler允許發送和處理Message,通過關聯線程的 MessageQueue 執行 Runnable 對象。 每個Hand ...
- Android Studio 2.3
- API 25
從源碼角度分析Handler機制。有利於使用Handler和分析Handler的相關問題。
Handler 簡介
一個Handler允許發送和處理Message,通過關聯線程的 MessageQueue 執行 Runnable 對象。
每個Handler實例都和一個單獨的線程及其消息隊列綁定。
可以將一個任務切換到Handler所在的線程中去執行。一個用法就是子線程通過Handler更新UI。
主要有2種用法:
- 做出計劃,在未來某個時間點執行消息和Runnable
- 在其他線程規劃並執行任務
要使用好Handler,需要瞭解與其相關的 MessageQueue
, Message
和Looper
;不能孤立的看Handler
Handler就像一個操作者(或者像一個對開發者開放的視窗),利用MessageQueue
和Looper
來實現任務調度和處理
// 這個回調允許你使用Handler時不新建一個Handler的子類
public interface Callback {
public boolean handleMessage(Message msg);
}
final Looper mLooper; // Handler持有 Looper 的實例
final MessageQueue mQueue; // 持有消息隊列
final Callback mCallback;
在Handler的構造器中,我們可以看到消息隊列是相關的Looper管理的
public Handler(Callback callback, boolean async) {
// 處理異常
mLooper = Looper.myLooper();
// 處理特殊情況...
mQueue = mLooper.mQueue; // 獲取的是Looper的消息隊列
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue; // 獲取的是Looper的消息隊列
mCallback = callback;
mAsynchronous = async;
}
Android是消息驅動的,實現消息驅動有幾個要素:
- 消息的表示:Message
- 消息隊列:MessageQueue
- 消息迴圈,用於迴圈取出消息進行處理:Looper
- 消息處理,消息迴圈從消息隊列中取出消息後要對消息進行處理:Handler
初始化消息隊列
在Looper構造器中即創建了一個MessageQueue
發送消息
通過Looper.prepare初始化好消息隊列後就可以調用Looper.loop進入消息迴圈了,然後我們就可以向消息隊列發送消息,
消息迴圈就會取出消息進行處理,在看消息處理之前,先看一下消息是怎麼被添加到消息隊列的。
消息迴圈
Java層的消息都保存在了Java層MessageQueue的成員mMessages中,Native層的消息都保存在了Native Looper的
mMessageEnvelopes中,這就可以說有兩個消息隊列,而且都是按時間排列的。
為什麼要用Handler這樣的一個機制
因為在Android系統中UI操作並不是線程安全的,如果多個線程併發的去操作同一個組件,可能導致線程安全問題。
為瞭解決這一個問題,android制定了一條規則:只允許UI線程來修改UI組件的屬性等,也就是說必須單線程模型,
這樣導致如果在UI界面進行一個耗時較長的數據更新等就會形成程式假死現象 也就是ANR異常,如果20秒中沒有完成
程式就會強制關閉。所以比如另一個線程要修改UI組件的時候,就需要藉助Handler消息機制了。
Handler發送和處理消息的幾個方法
1.void handleMessage( Message msg):處理消息的方法,該方法通常被重寫。
2.final boolean hasMessage(int what):檢查消息隊列中是否包含有what屬性為指定值的消息
3.final boolean hasMessage(int what ,Object object) :檢查消息隊列中是否包含有what好object屬性指定值的消息
4.sendEmptyMessage(int what):發送空消息
5.final Boolean send EmptyMessageDelayed(int what ,long delayMillis):指定多少毫秒發送空消息
6.final boolean sendMessage(Message msg):立即發送消息
7.final boolean sendMessageDelayed(Message msg,long delayMillis):多少秒之後發送消息
與Handler工作的幾個組件Looper、MessageQueue各自的作用:
- 1.Handler:它把消息發送給Looper管理的MessageQueue,並負責處理Looper分給它的消息
- 2.MessageQueue:管理Message,由Looper管理
- 3.Looper:每個線程只有一個Looper,比如UI線程中,系統會預設的初始化一個Looper對象,它負責管理MessageQueue,
不斷的從MessageQueue中取消息,並將相對應的消息分給Handler處理
Handler.java (frameworks/base/core/java/android/os)
// 將消息添加到隊列前,先判斷隊列是否為null
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; // 將自己指定為Message的Handler
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
從這裡也不難看出,每個Message都持有Handler。如果Handler持有Activity的引用,Activity onDestroy後Message卻仍然在隊列中,
因為Handler與Activity的強關聯,會造成Activity無法被GC回收,導致記憶體泄露。
因此在Activity onDestroy 時,與Activity關聯的Handler應清除它的隊列由Activity產生的任務,避免記憶體泄露。
消息隊列 MessageQueue.java (frameworks/base/core/java/android/os)
// 添加消息
boolean enqueueMessage(Message msg, long when) {
// 判斷並添加消息...
return true;
}
Handler.sendEmptyMessage(int what) 流程解析
獲取一個Message實例,並立即將Message實例添加到消息隊列中去。
簡要流程如下
// Handler.java
// 立刻發送一個empty消息
sendEmptyMessage(int what)
// 發送延遲為0的empty消息 這個方法里通過Message.obtain()獲取一個Message實例
sendEmptyMessageDelayed(what, 0)
// 計算消息的計劃執行時間,進入下一階段
sendMessageDelayed(Message msg, long delayMillis)
// 在這裡判斷隊列是否為null 若為null則直接返回false
sendMessageAtTime(Message msg, long uptimeMillis)
// 將消息添加到隊列中
enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
// 接下來是MessageQueue添加消息
// MessageQueue.java
boolean enqueueMessage(Message msg, long when)
部分源碼如下
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 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) {
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);
}
Handler 取消任務 removeCallbacksAndMessages
要取消任務時,調用下麵這個方法
public final void removeCallbacksAndMessages(Object token) {
mQueue.removeCallbacksAndMessages(this, token);
}
通過調用Message.recycleUnchecked()
方法,取消掉與此Handler相關聯的Message。
相關的消息隊列會執行取消指令
void removeCallbacksAndMessages(Handler h, Object object)
Message 和 MessageQueue 簡介
Message
Message 屬於被傳遞,被使用的角色
Message 是包含描述和任意數據對象的“消息”,能被髮送給Handler
。
包含2個int屬性和一個額外的對象
雖然構造器是公開的,但獲取實例最好的辦法是調用Message.obtain()
或Handler.obtainMessage()
。
這樣可以從他們的可回收對象池中獲取到消息實例
一般來說,每個Message實例握有一個Handler
部分屬性值
/*package*/ Handler target; // 指定的Handler
/*package*/ Runnable callback;
// 可以組成鏈表
// sometimes we store linked lists of these things
/*package*/ Message next;
重置自身的方法,將屬性全部重置
public void recycle()
void recycleUnchecked()
獲取Message實例的常用方法,得到的實例與傳入的Handler綁定
/**
* Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned.
* @param h Handler to assign to the returned Message object's <em>target</em> member.
* @return A Message object from the global pool.
*/
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
將消息發送給Handler
/**
* Sends this Message to the Handler specified by {@link #getTarget}.
* Throws a null pointer exception if this field has not been set.
*/
public void sendToTarget() {
target.sendMessage(this); // target 就是與消息綁定的Handler
}
調用這個方法後,Handler會將消息添加進它的消息隊列MessageQueue
中
MessageQueue
持有一列可以被Looper分發的Message。
一般來說由Handler將Message添加到MessageQueue中。
獲取當前線程的MessageQueue方法是Looper.myQueue()
Looper 簡介
Looper與MessageQueue緊密關聯
在一個線程中運行的消息迴圈。線程預設情況下是沒有與之管理的消息迴圈的。
要創建一個消息迴圈,線上程中調用prepare,然後調用loop。即開始處理消息,直到迴圈停止。
大多數情況下通過Handler來與消息迴圈互動。
Handler與Looper線上程中交互的典型例子
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare(); // 為當前線程準備一個Looper
// 創建Handler實例,Handler會獲取當前線程的Looper
// 如果實例化Handler時當前線程沒有Looper,會報異常 RuntimeException
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop(); // Looper開始運行
}
}
Looper中的屬性
Looper持有MessageQueue;唯一的主線程Looper sMainLooper
;Looper當前線程 mThread
;
存儲Looper的sThreadLocal
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue; // Handler會獲取這個消息隊列實例(參考Handler構造器)
final Thread mThread; // Looper當前線程
ThreadLocal並不是線程,它的作用是可以在每個線程中存儲數據。
Looper 方法
準備方法,將當前線程初始化為Looper。退出時要調用quit
public static void prepare() {
prepare(true);
}
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實例存入了sThreadLocal
}
prepare
方法新建 Looper 並存入 sThreadLocal sThreadLocal.set(new Looper(quitAllowed))
ThreadLocal<T>
類
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
當要獲取Looper對象時,從sThreadLocal
獲取
// 獲取與當前線程關聯的Looper,返回可以為null
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
在當前線程運行一個消息隊列。結束後要調用退出方法quit()
public static void loop()
準備主線程Looper。Android環境會創建主線程Looper,開發者不應該自己調用這個方法。
UI線程,它就是ActivityThread,ActivityThread被創建時就會初始化Looper,這也是在主線程中預設可以使用Handler的原因。
public static void prepareMainLooper() {
prepare(false); // 這裡表示了主線程Looper不能由開發者來退出
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
獲取主線程的Looper。我們開發者想操作主線程時,可調用此方法
public static Looper getMainLooper()
請參考: http://rustfisher.github.io/2017/06/07/Android_note/Android-Handler/