Android handler機制流程詳解
handler機制由以下幾部分組成:
.Handler
.Message
.MessageQueue
.Looper
總體流程介紹:
一、程序啟動時
在main方法中為主線程建立了對應自己線程的Looper,在Looper被建立的同時會建立一個MessageQueue消息隊列并持有。同時在main方法中調用了Looper.loop()進行一個死循環周遊Looper自己持有的消息隊列。
二、建立handler
如果在主線程中建立,handler能擷取到主線程對應的looper并持有。如果是在子線程中建立handler,則需要自己調用建立looper方法和自己調用Looper.loop。否則會報錯。
三、建立Message并發送
建立的Message将建立自己的handler資訊封裝進Message内部,最終被發送到了MessageQueue消息隊列中。
四、接收消息
在Looper.loop中,從MessageQueue中拿出Message,并根據其中對應的handler資訊進行分發處理
對應關系:
一條線程Thread對應唯一的一個Looper,其對應相關的資訊存儲在ThreadLocal中。
一個Looper對象持有一個自己的MessageQueue。
在同一條線程中,無論建立多少handler,都隻對應一個Looper和一個MessageQueue。
是以,handler在被建立時就根據Looper與對應的線程進行了綁定,即使在别的線程根據該handler發送了消息,也會被發送到handler對應的Looper的MessageQueue消息隊列中并被分發處理。
代碼分析:
接下來,舉一個簡單的例子,并且簡單分析一下主流程對應的原碼:
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
}
}
};
new Thread() {
public void run() {
Message message = handler.obtainMessage();
message.what = 100;
message.obj = "測試+ : " + i;
handler.sendMessage(message);
}
}.start();
解析:
Handler:負責發送消息和接收傳回的消息,主流程部分源碼分析
public class Handler {
//主要變量
final Looper mLooper;//與每個線程對應的looper
final MessageQueue mQueue;//消息隊列
final Callback mCallback;//是否同步
//構造方法
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
..
}
mLooper = Looper.myLooper();//根據目前線程資訊在ThreadLocal中擷取線程對應的looper
注:
.如果是在子線程中建立,需要調用Looper.prepare( boolean quitAllowed)
.該方法的作用是擷取調用方法的線程資訊,判斷目前線程是否有looper,沒有則建立。
.在ActivityThread中建立過主線程的looper
if (mLooper == null) {
..
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
//從消息對象池中取出消息
public final Message obtainMessage()
{
return Message.obtain(this);
}
//發送消息的各種方法
handler.sendMessage(message);
handler.sendMessageDelayed(
Message msg, long delayMillis);
handler.sendMessageAtTime(
Message msg, long uptimeMillis);
handler.sendEmptyMessage(
int what);
handler.sendEmptyMessageDelayed(
int what, long delayMillis);
handler.sendMessageAtFrontOfQueue(
Message msg);
handler.sendEmptyMessageAtTime(
int what, long uptimeMillis);
//最終都是調用來發送
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;//将目前handler資訊封裝進Message
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
//分發消息
/**
* 該方法在Looper中next()方法中被調用 根據msg.target來區分
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
}
主要發生的大事:
一、當調用:new Handler() 時:
1、Handler中:Looper.myLooper();
在ThreadLocal中擷取目前線程對應的looper,主線程對應的looper在main方法中已經被建立
2、mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
取出looper中的消息隊列(mQueue)。Callback對象。是否同步。封裝進該handler對象
二、當調用:handler.obtainMessage()時:
在消息對象池中擷取消息。
三、當調用:sendMessage或者各種send方法時:
最終調用 enqueueMessage,将目前handler對象封裝進message.target,
并調用mQueue.enqueueMessage(msg, uptimeMillis),将消息發送給消息隊列
Message:負責封裝消息,主流程部分源碼分析
public final class Message implements Parcelable {
public int what;
public int arg1;
public int arg2;
public Object obj;
long when;
Bundle data;
Handler target;
Runnable callback;
Message next;
public static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
private static boolean gCheckRecycle = true;
//被handler調用
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
/**
* 判斷消息對象池中是否有消息,并從消息對象池中擷取消息
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
省略其他好多方法...
結論:在handler中使用obtain(this)來擷取消息池中消息對象時,将目前this封裝進message.target中
Looper:不斷循環,從消息隊列中擷取消息,并根據消息的target來進行消息分發,即調用對應handler的dispatchMessage方法。主流程部分源碼分析
public final class Looper {
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // 主線程looper對象
final MessageQueue mQueue; //消息隊列
final Thread mThread;//目前線程
//構造方法:隻有一個構造,并且建立mQueue 和給mThread 指派
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
//初始化,如果handler在子線程中被調用,則要先調用此方法
public static void prepare() {
prepare(true);
}
//被上面方法調用
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
//該方法内部根據目前線程資訊判斷是否建立了looper對象,如果有建立,則傳回對應looper對象。
//如果沒有建立,則傳回空
throw new RuntimeException("Only one Looper may be created per thread");
}
//建立一個looper對象,并将目前線程和該looper綁定
sThreadLocal.set(new Looper(quitAllowed));
}
//主線程在建立的時候在ActivityThread類中main方法中被調用
建立主線程對應的looper和給目前sMainLooper指派
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
//handler中擷取looper的方法
public static @Nullable
Looper myLooper() {
//該方法内部根據目前線程資訊判斷是否建立了looper對象,如果有建立,則傳回對應looper對象。
//如果沒有建立,則傳回空
return sThreadLocal.get();
}
//開啟loop循環查詢隊列,
主線程對應的loop方法在main中被調用,如果在子線程中建立的handler,需要自己調用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;
省略衆多代碼....
for (; ; ) {
//隊列的next()方法中處理基本邏輯,如果有消息:傳回。如果沒有消息:阻塞||等待
Message msg = queue.next();
if (msg == null) {
return;
}
省略衆多代碼.....
try {
msg.target.dispatchMessage(msg);//根據targer(即handler)進行分發
..
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//将消息放回消息池
msg.recycleUnchecked();
}
}
}
該類負責做的事情:
1、在prepare()被調用時:
該方法可以定義為:建立調用線程對應的looper,判斷被調用的線程是否有looper對象,如果有就提示錯誤,沒有則進行建立并綁定線程和looper。
2、構造被調用時:
建立 MessageQueue 消息隊列,和給mThread變量(目前線程)指派
3、在myLooper()被調用時:
該方法可以定義為擷取線程對應的looper,判斷被調用的線程是否有looper對象,如果有就傳回,沒有傳回空。
4、prepareMainLooper()方法在main方法中被調用,建立主線程對應的looper。
5、loop()方法在main中被調用,作用為死循環從目前線程對應looper持有的消息隊列中取出消息,并根據消息對應的targer進行分發。
注:當源碼解析到這裡有一個疑問:為什麼在主線程裡面Looper的死循環不會引發ANR
解釋:Looper.loop無限循環為什麼沒有引發ANR
MessageQueue:消息隊列,接收handler發送過來的消息存入隊列,死循環周遊消息隊列傳回給looper,消息 隊列沒有消息時,阻塞||等待。主流程部分源碼分析
public final class MessageQueue {
private final boolean mQuitAllowed;
是否允許退出
private boolean mQuitting;//是否放棄消息隊列
@SuppressWarnings("unused")
private long mPtr;
private native static long nativeInit();//傳回一個NativeMessageQueue,具體實作過程在Nativity層
private native static void nativeWake(long ptr);//喚醒對應線程
//構造
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;//是否允許退出
mPtr = nativeInit();//傳回一個NativeMessageQueue,具體實作過程在Nativity層
}
//該方法就是在handler中調用send系列方法最終調用的方法,将消息放入消息隊列
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {//msg.target就是發送此消息的Handler
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {//表示此消息正在被使用
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
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();
return false;
}
msg.markInUse();
msg.when = when;//将延遲時間封裝到msg内部
Message p = mMessages;//消息隊列的第一個元素
boolean needWake;//是否需要喚醒線程
if (p == null || when == 0 || when < p.when) {
//如果此隊列中頭部元素是null(空的隊列,一般是第一次),
或者此消息不是延時的消息,則此消息需要被立即處理,
此時會将這個消息作為新的頭部元素,并将此消息的next指向舊的頭部元素,
然後判斷如果Looper擷取消息的線程如果是阻塞狀态則喚醒它,讓它立刻去拿消息處理。
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//如果此消息是延時的消息,則将其添加到隊列中,
原理就是連結清單的添加新元素,按照when,也就是延遲的時間來插入的,
延遲的時間越長,越靠後,這樣就得到一條有序的延時消息連結清單,
取出消息的時候,延遲時間越小的,就被先擷取了。插入延時消息不需要喚醒Looper線程
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (; ; ) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);//喚醒線程
}
}
return true;
}
//該方法就是在Looper中被調用的方法,傳回一個message,如果隊列為空,則阻塞
Message next() {
final long ptr = mPtr;
//從注釋可以看出,隻有looper被放棄的時候(調用了quit方法)才傳回null,
mPtr是MessageQueue的一個long型成員變量,關聯的是一個在C++層的MessageQueue,
阻塞操作就是通過底層的這個MessageQueue來操作的;當隊列被放棄的時候其變為0
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
for (; ; ) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞方法,主要是通過native層的epoll監聽檔案描述符的寫入事件來實作的。
//如果nextPollTimeoutMillis=-1,一直阻塞不會逾時。
//如果nextPollTimeoutMillis=0,不會阻塞,立即傳回。
//如果nextPollTimeoutMillis>0,最長阻塞nextPollTimeoutMillis毫秒(逾時),如果期間有程式喚醒會立即傳回。
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) {
//msg.target == null表示此消息為消息屏障(通過postSyncBarrier方法發送來的)
//如果發現了一個消息屏障,會循環找出第一個異步消息(如果有異步消息的話),所有同步消息都将忽略(平常發送的一般都是同步消息)
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 如果消息此刻還沒有到時間,設定一下阻塞時間nextPollTimeoutMillis,進入下次循環的時候會調用
//nativePollOnce(ptr, nextPollTimeoutMillis)進行阻塞;
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//正常取出消息
//設定mBlocked = false代表目前沒有阻塞
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 {
//沒有消息,會一直阻塞,直到被喚醒
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
}
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
}
主要做的事:
MessageQueue() 構造:在Looper建立時被建立
enqueueMessage():handler一系列send方法最終殊途同歸到這個方法,将消息放入消息隊列
next():在Looper.loop() 方法中被調用,周遊隊列并傳回消息,如果隊列中沒有消息則阻塞