1.Handler是什麼?
Handler機制主要為線程間通信而生,是Android中定義的一套消息傳遞機制。
主要是為了解決子線程執行完耗時操作後,怎麼回調到主(UI)線程的問題。
2.Handler機制的主要成員有哪些?
Handler、Looper、Message和MessageQueue
Handler:負責發送Message到MessageQueue,同時負責接收消息
Looper:負責從MessageQueue讀取消息,并通過Message的target,調用handler的消息分發處理
Message:消息載體
MessageQueue:消息隊列
3.handler消息機制的主要流程是什麼?
消息發送過程:Handler在子線程發送Message,由于Handler初始化的時候有持有目前線程的Looper和Looper中的 MessageQueue,而這個Handler在主線程初始化,也就是該Handler持有主線程Looper和MessageQueue,發送普通消息會給 Message的target指派Handler本身,發送消息會最終會調用MessageQueue的enqueueMessage方法。
消息插入過程:根據系統相對時間+延遲時間 按照順序 插入到單連結清單MessageQueue中,如果在表頭,會判斷是否有阻塞,有 阻 塞會調用native層的nativeWake方法喚醒線程,如果在表中間或表尾會判斷該消息是否是異步消息,且阻塞的話,也會調用 native層的nativeWake方法喚醒線程。
消息取出過程:Looper循環loop方法,會調用MessageQueue的next方法,這個方法在源碼上面顯示有可能會阻塞, MessageQueue next 也有一個死循環操作,如果上一個取出的消息有阻塞時間,會先調用native層的nativePollOnce方法進行 阻塞,然後會判斷是否是屏障Message,如果是屏障消息先過濾掉普通消息,先擷取異步消息。如果Message消息的when大于 目前系統相對時間則會設定需要阻塞的時間,如果Message消息的when小于或者等于目前系統相對時間,會取出這個消息。如 果有阻塞的情況,還會調用IdleHandler的回調。通過MessageQueue取出Messgae後,會通過Message的target(target就是發 送該 Message的Handler)調用 Handler的dispatchMessage方法處理消息分發。
消息分發過程:如果Message callback不為空,代表是通過post的方式發送消息,調用Runnable的 run方法;如果Handler中的 mCallback不為空,調用mCallbcak的 handleMessage;否則會調用handleMessage回調。
4.延時消息的實作 是在插入MessageQueue的時候,還是Looper取的時候?
取消息的時候實作的!
插入的時候會根據相對時間+延遲時間作為message的屬性,并根據該屬性進行排序。
真正的延時是在Looper取消息的時候進行判斷是否為延時消息,然後調用native層的nativePollOnce方法進行阻塞。
5.發送消息用send 和 post 有什麼差別?
send方式和post方式本質沒有差別,都是發送Message,隻不過post方式會把Runnable對象指派給Message的callback,在 最後消息分發的時候會回調Runnable的run方法。
6.為什麼建議用obtain方法建立Message?
Message 本身包含兩個Message對象,一個是sPool,一個是next,但通過看源碼可知道sPool是一個static對象,是所有對象共 有,Message sPool就是一個單連結清單結構,Message就是單連結清單中的一個節點。
使用obtain方法,取的是Message 的 sPool ,改變sPool指向sPool的next,取出sPool本身,并清空該Message的flags和next。 這樣的好處是是可避免重複建立多個執行個體對象,可以取消息池子之前已經存在的消息。
7.子線程能直接建立Handler并使用嗎?
(1)直接建立會報錯!
在建立Handler方法中,Handler會持有該線程的Looper 和 Looper中的MessageQueue,Handler的使用必須是結合 Looper,而子線程中并沒有綁定Looper,是以會報錯。
因為Handler的構造方法中可以傳遞Looper:handler對象所綁定的線程其實并不取決于該handler對象由哪個線程建構,而 是取決于該handler對象所綁定的Looper屬于哪個線程。
handleMessage 方法執行的線程一定是在建立Handler的線程嗎?答案是否定的,是在綁定Looper所屬于的那個線程。
(2)想要建立需要幹嘛
1. 在建立Handler前首先調用Looper.prepare()建立Looper
2. 然後調用Looper.loop()開啟循環(隻要保證發消息之前調用過該方法即可,否則Looper不運轉,
注意:不必糾結這一步是在new Handler前 還是在 new Handler後)
兩步缺一不可!
8.Looper是如何確定一個線程隻能建立一個的?MessageQueue呢?
1. 利用 ThreadLocal特性。(1)判斷ThreadLocal裡面是否為空(2)把Looper放到ThreadLocal中,
2. 利用私有構造方法 隻能在類裡面被通路,無法被類外通路。
3. ThreadLocal 的詳細解釋:https://blog.csdn.net/wsq_tomato/article/details/82390262
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
9.HandlerThread和普通線程有何差別?
其實就是一個自己封裝好Looper的線程,源碼如下
構造方法:
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
run方法:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
10.Handler同步屏障
(1)Message消息主要分為三種:同步消息、屏障消息、異步消息
(2)同步屏障就是通過屏障消息來屏蔽所有同步消息,處理後續異步消息
核心代碼:
首先是 MessageQueue 取消息的代碼:
@UnsupportedAppUsage
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
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) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
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 {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
最核心的是的判斷是這段代碼:
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
屏障消息主要是起一個屏蔽作用,是以不需要有handler 處理消息分發,是以target為null。
如果有屏障消息,且消息不是異步的則繼續取下一個,直到遇到異步消息。
異步消息如何設定?:
(1) 可以直接調用Message的setAsynchronous方法
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
(2) Handler的構造方法也可以設定 mAsynchronous,但是是hide标簽修飾的方法,我們可以通過反射去調用,也是可以的。
同步屏障的應用?:
比如:螢幕重新整理機制
Android 每隔16.6ms會重新整理一次螢幕,每個Activity對應一個 DecorView 根布局View樹。
初始化過程中DecorView會被添加到viewRootImp(根視圖);
根視圖setView的過程:
viewRootImp.setView() —>
viewRootImp.requestLayout() —>
viewRootImp.scheduleTraversals() —>
viewRootImp.doTraversal() —>
viewRootImp.performTraversals()—>
主要看scheduleTraversals這個方法:
@UnsupportedAppUsage
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//設定同步屏障
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//發送異步消息
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
其中“ mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); ”這行代碼就是設定同步屏障。
然後咱們對這行代碼進行跟蹤:
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
@TestApi
public void postCallback(int callbackType, Runnable action, Object token) {
postCallbackDelayed(callbackType, action, token, 0);
}
@TestApi
public void postCallbackDelayed(int callbackType,
Runnable action, Object token, long delayMillis) {
if (action == null) {
throw new IllegalArgumentException("action must not be null");
}
if (callbackType < 0 || callbackType > CALLBACK_LAST) {
throw new IllegalArgumentException("callbackType is invalid");
}
postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
if (DEBUG_FRAMES) {
Log.d(TAG, "PostCallback: type=" + callbackType
+ ", action=" + action + ", token=" + token
+ ", delayMillis=" + delayMillis);
}
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
到最後發送了一個異步消息。
因為 同步屏障的作用,是以 異步消息的執行優先于同步消息,保證了螢幕重新整理的及時性和優先性。
完畢,如果哪些地方寫錯了,請各位指出,深海謝過各位同行。