Handler屬於八股文中很是經典的一個考題了,致使這個知識點不少時候,考官都懶得問了;這玩意好久以前就看過,可是過了一段時間,就很容易忘記,可是處理內存泄漏,IdleHandler之類的考點答案確定很難忘。。。雖然考官不少時候不屑問,可是要是問到了,你忘了且不知道怎麼回答,那就很尷尬了java
鄙人也來炒個剩飯,力求通俗易懂的來描述下Handler機制的整個流程,相關知識點,畫了一些流程圖,時序圖來展現其運行機制,讓本文圖文並茂!android
文章中關鍵方法源碼,能夠直接點擊方法名,跳轉查看對應方法的源碼安全
若是看了沒收穫,噴我!多線程
開頭須要創建個handler做用的整體印象,下面畫了一個整體的流程圖less
從上面的流程圖能夠看出,整體上是分幾個大塊的異步
相關知識點大概涉及到這些,下面詳細講解下!async
先來看下使用,否則源碼,原理圖搞了一大堆,一時想不起怎麼用的,就尷尬了ide
使用很簡單,此處僅作個展現,你們能夠熟悉下函數
演示代碼儘可能簡單是爲了演示,關於內部類持有弱引用或者銷燬回調中清空消息隊列之類,就不在此處展現了oop
Handler.java ... public void dispatchMessage(@NonNull Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } ...
從上面源碼可知,handler的使用總的來講,分倆大類,細分三小類
public class MainActivity extends AppCompatActivity { private TextView msgTv; private Handler mHandler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); msgTv = findViewById(R.id.tv_msg); //消息收發一體 new Thread(new Runnable() { @Override public void run() { String info = "第一種方式"; mHandler.post(new Runnable() { @Override public void run() { msgTv.setText(info); } }); } }).start(); } }
public class MainActivity extends AppCompatActivity { private TextView msgTv; private Handler mHandler = new Handler(new Handler.Callback() { //接收消息,刷新UI @Override public boolean handleMessage(@NonNull Message msg) { if (msg.what == 1) { msgTv.setText(msg.obj.toString()); } //false 重寫Handler類的handleMessage會被調用, true 不會被調用 return false; } }); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); msgTv = findViewById(R.id.tv_msg); //發送消息 new Thread(new Runnable() { @Override public void run() { Message message = Message.obtain(); message.what = 1; message.obj = "第二種方式 --- 1"; mHandler.sendMessage(message); } }).start(); } }
public class MainActivity extends AppCompatActivity { private TextView msgTv; private Handler mHandler = new Handler() { //接收消息,刷新UI @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); if (msg.what == 1) { msgTv.setText(msg.obj.toString()); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); msgTv = findViewById(R.id.tv_msg); //發送消息 new Thread(new Runnable() { @Override public void run() { Message message = Message.obtain(); message.what = 1; message.obj = "第二種方式 --- 2"; mHandler.sendMessage(message); } }).start(); } }
你們確定有印象,在子線程和子線程的通訊中,就必須在子線程中初始化Handler,必須這樣寫
new Thread(new Runnable() { @Override public void run() { Looper.prepare(); Handler handler = new Handler(); Looper.loop(); } });
ActivityThread.java ... public static void main(String[] args) { ... //主線程Looper Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } //主線程的loop開始循環 Looper.loop(); ... } ...
爲何要使用prepare和loop?我畫了個圖,先讓你們有個總體印象
具體看下每一個步驟的源碼,這裏也會標定好連接,方便你們隨時過去查看
Looper.java ... 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)); } ...
Handler.java ... @Deprecated public Handler() { this(null, false); } public Handler(@Nullable Callback callback, boolean async) { 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()); } } mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; } ...
Looper.java ... public static @Nullable Looper myLooper() { return sThreadLocal.get(); } ...
分發消息
裏講
Looper.java ... public static void loop() { final Looper me = myLooper(); ... final MessageQueue queue = me.mQueue; ... for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } ... try { msg.target.dispatchMessage(msg); if (observer != null) { observer.messageDispatched(token, msg); } dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } catch (Exception exception) { if (observer != null) { observer.dispatchingThrewException(token, msg, exception); } throw exception; } finally { ThreadLocalWorkSource.restore(origWorkSource); if (traceTag != 0) { Trace.traceEnd(traceTag); } } .... msg.recycleUnchecked(); } } ...
收發消息的操做口都在Handler裏,這是咱們最直觀的接觸的點
下方的思惟導圖總體作了個歸納
發送消息涉及到倆個方法:post(...)和sendMessage(...)
Handler.java ... //post public final boolean post(@NonNull Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } //生成Message對象 private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; } //sendMessage方法 public final boolean sendMessage(@NonNull Message msg) { return sendMessageDelayed(msg, 0); } public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } public boolean sendMessageAtTime(@NonNull 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); } ///將Message加入詳細隊列 private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) { //設置target msg.target = this; msg.workSourceUid = ThreadLocalWorkSource.getUid(); if (mAsynchronous) { //設置爲異步方法 msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); } ...
MessageQueue.java ... boolean enqueueMessage(Message msg, long when) { ... synchronized (this) { ... msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. 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; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; } ...
接受消息相對而言就簡單多
Handler.java ... public void dispatchMessage(@NonNull Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } ...
handleCallback(msg)
mCallback.handleMessage(msg)
handleMessage(msg)
消息分發是在loop()中完成的,來看看loop()這個重要的方法
Looper.java ... public static void loop() { final Looper me = myLooper(); ... final MessageQueue queue = me.mQueue; ... for (;;) { //遍歷消息池,獲取下一可用消息 Message msg = queue.next(); // might block ... try { //分發消息 msg.target.dispatchMessage(msg); ... } catch (Exception exception) { ... } finally { ... } .... //回收消息,進圖消息池 msg.recycleUnchecked(); } } ...
遍歷消息的關鍵方法確定是下面這個
來看看這個Message中的next()方法吧
MessageQueue.java ... Message next() { final long ptr = mPtr; ... int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { ... //阻塞,除非到了超時時間或者喚醒 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; // 這是關於同步屏障(SyncBarrier)的知識,放在同步屏障欄目講 if (msg != null && msg.target == null) { do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) { //每一個消息處理有耗時時間,之間存在一個時間間隔(when是將要執行的時間點)。 //若是當前時刻還沒到執行時刻(when),計算時間差值,傳入nativePollOnce定義喚醒阻塞的時間 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { mBlocked = false; //該操做是把異步消息單獨從消息隊列裏面提出來,而後返回,返回以後,該異步消息就從消息隊列裏面剔除了 //mMessage仍處於未分發的同步消息位置 if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); //返回符合條件的Message return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } //這是處理調用IdleHandler的操做,有幾個條件 //一、當前消息隊列爲空(mMessages == null) //二、已經到了能夠分發下一消息的時刻(now < mMessages.when) 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); } 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; } }
總結下源碼裏面表達的意思
這裏簡單的畫了個流程圖
分發消息主要的代碼是: msg.target.dispatchMessage(msg);
也就是說這是Handler類中的dispatchMessage(msg)方法
public void dispatchMessage(@NonNull Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
能夠看到,這裏的代碼,在收發消息欄目的接受消息那塊已經說明過了,這裏就無須重複了
msg.recycleUnchecked()是處理完成分發的消息,完成分發的消息並不會被回收掉,而是會進入消息池,等待被複用
Message.java ... void recycleUnchecked() { // Mark the message as in use while it remains in the recycled object pool. // Clear out all other details. flags = FLAG_IN_USE; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUid = UID_NONE; workSourceUid = UID_NONE; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } }
來看下消息池回收消息圖示
既然有將已使用的消息回收到消息池的操做,那確定有獲取消息池裏面消息的方法了
Message.java ... 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(); }
來看下從消息池取一個消息的圖示
在MessageQueue類中的next方法裏,能夠發現有關於對IdleHandler的處理,你們可千萬別覺得它是什麼Handler特殊形式之類,這玩意就是一個interface,裏面抽象了一個方法,結構很是的簡單
MessageQueue.java ... Message next() { final long ptr = mPtr; ... int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { ... //阻塞,除非到了超時時間或者喚醒 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; ... //這是處理調用IdleHandler的操做,有幾個條件 //一、當前消息隊列爲空(mMessages == null) //二、未到到了能夠分發下一消息的時刻(now < mMessages.when) //三、pendingIdleHandlerCount < 0代表:只會在此for循環裏執行一次處理IdleHandler操做 if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } 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); } } } pendingIdleHandlerCount = 0; nextPollTimeoutMillis = 0; } }
實際上從上面的代碼裏面,能夠分析出不少信息
IdleHandler相關信息
調用條件
實現了IdleHandler中的queueIdle方法
保活
狀態IdleHandler代碼
MessageQueue.java ... /** * Callback interface for discovering when a thread is going to block * waiting for more messages. */ public static interface IdleHandler { /** * Called when the message queue has run out of messages and will now * wait for more. Return true to keep your idle handler active, false * to have it removed. This may be called if there are still messages * pending in the queue, but they are all scheduled to be dispatched * after the current time. */ boolean queueIdle(); } public void addIdleHandler(@NonNull IdleHandler handler) { if (handler == null) { throw new NullPointerException("Can't add a null IdleHandler"); } synchronized (this) { mIdleHandlers.add(handler); } } public void removeIdleHandler(@NonNull IdleHandler handler) { synchronized (this) { mIdleHandlers.remove(handler); } }
怎麼使用IdleHandler呢?
public class MainActivity extends AppCompatActivity { private TextView msgTv; private Handler mHandler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); msgTv = findViewById(R.id.tv_msg); //添加IdleHandler實現類 mHandler.getLooper().getQueue().addIdleHandler(new InfoIdleHandler("我是IdleHandler")); mHandler.getLooper().getQueue().addIdleHandler(new InfoIdleHandler("我是大帥比")); //消息收發一體 new Thread(new Runnable() { @Override public void run() { String info = "第一種方式"; mHandler.post(new Runnable() { @Override public void run() { msgTv.setText(info); } }); } }).start(); } //實現IdleHandler類 class InfoIdleHandler implements MessageQueue.IdleHandler { private String msg; InfoIdleHandler(String msg) { this.msg = msg; } @Override public boolean queueIdle() { msgTv.setText(msg); return false; } } }
來到最複雜的模塊了
在理解同步屏障的概念前,咱們須要先搞懂幾個前置知識
什麼是同步消息?什麼是異步消息?
public boolean isAsynchronous() { return (flags & FLAG_ASYNCHRONOUS) != 0; }
msg.setAsynchronous(true); public void setAsynchronous(boolean async) { if (async) { flags |= FLAG_ASYNCHRONOUS; } else { flags &= ~FLAG_ASYNCHRONOUS; } }
Message msg = Message.obtain(); //設置異步消息標記 msg.setAsynchronous(true);
咱們正常狀況下,不多會使用setAsynchronous
方法的,那麼在不使用該方法的時候,消息的默認類型是什麼呢?
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) { msg.target = this; msg.workSourceUid = ThreadLocalWorkSource.getUid(); if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
public Handler(@Nullable Callback callback, boolean async) { 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()); } } mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; } public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; }
public Handler() { this(null, false); } public Handler(@NonNull Looper looper) { this(looper, null, false); } public Handler(@NonNull Looper looper, @Nullable Callback callback) { this(looper, callback, false); }
在next方法中發現,target爲null的消息被稱爲同步屏障消息,那他爲啥叫同步屏障消息呢?
MessageQueue.java ... @UnsupportedAppUsage @TestApi public int postSyncBarrier() { return postSyncBarrier(SystemClock.uptimeMillis()); } private int postSyncBarrier(long when) { // Enqueue a new sync barrier token. // We don't need to wake the queue because the purpose of a barrier is to stall it. synchronized (this) { final int token = mNextBarrierToken++; final Message msg = Message.obtain(); msg.markInUse(); msg.when = when; msg.arg1 = token; Message prev = null; Message p = mMessages; if (when != 0) { while (p != null && p.when <= when) { prev = p; p = p.next; } } if (prev != null) { // invariant: p == prev.next msg.next = p; prev.next = msg; } else { msg.next = p; mMessages = msg; } return token; } }
mMessage這個變量,代表是將要被處理的消息,將要被返回的消息,也能夠認爲,他是未處理消息隊列的頭結點消息
關於同步屏障消息
同步屏障消息插入消息隊列流程圖
MessageQueue.java ... Message next() { final long ptr = mPtr; ... int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { ... //阻塞,除非到了超時時間或者喚醒 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; // 這是關於同步屏障(SyncBarrier)的邏輯塊 if (msg != null && msg.target == null) { do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) { //每一個消息處理有耗時時間,之間存在一個時間間隔(when是將要執行的時間點)。 //若是當前時刻還沒到執行時刻(when),計算時間差值,傳入nativePollOnce定義喚醒阻塞的時間 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { mBlocked = false; //該操做是把異步消息單獨從消息隊列裏面提出來,而後返回,返回以後,該異步消息就從消息隊列裏面剔除了 //mMessage仍處於未分發的同步消息位置 if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); //返回符合條件的Message return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } ... } ... } }
去掉大量咱們無需關注的代碼,發現這也沒啥嘛,就是一堆if eles for之類的,來分析分析
文字寫了一大堆,我也是儘量詳細描述,同步屏障邏輯代碼塊會產生的影響,整個圖,加深下印象!
那麼這個同步屏障有什麼做用呢?
有個急需的問題,就是什麼地方用到了postSyncBarrier(long when)方法,這個方法對外是不暴露的,只有內部包可以調用
搜索了整個源碼包,發現只有幾個地方使用了它,剔除測試類,MessageQueue類,有做用的就是:ViewRootImpl類和Device類
pauseEvents():Device內部涉及的是打開設備的時候,會添加一個同步屏障消息,屏蔽後續全部的同步消息處理
同步屏障添加:開機時,添加同步屏障
Device.java ... private class DeviceHandler extends Handler { ... @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_OPEN_DEVICE: ... pauseEvents(); break; ... } } public void pauseEvents() { mBarrierToken = getLooper().myQueue().postSyncBarrier(); } public void resumeEvents() { getLooper().myQueue().removeSyncBarrier(mBarrierToken); mBarrierToken = 0; } }
同步屏障移除:完成開機後,移除同步屏障
Device.java ... private class DeviceHandler extends Handler { ... public void pauseEvents() { mBarrierToken = getLooper().myQueue().postSyncBarrier(); } public void resumeEvents() { getLooper().myQueue().removeSyncBarrier(mBarrierToken); mBarrierToken = 0; } } private class DeviceCallback { public void onDeviceOpen() { mHandler.resumeEvents(); } .... }
Device中使用同步屏障總體過程比較簡單,這裏簡單描述下
該欄目的分析,必須引用一個很是重要的結論,給出該結論的文章:源碼分析_Android UI什麼時候刷新_Choreographer
scheduleTraversals():很是重要的方法
ViewRootImpl.java ... void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } }
結論:源碼分析_Android UI什麼時候刷新_Choreographer
咱們調用View的requestLayout或者invalidate時,最終都會觸發ViewRootImp執行scheduleTraversals()方法。這個方法中ViewRootImp會經過Choreographer來註冊個接收Vsync的監聽,當接收到系統體層發送來的Vsync後咱們就執行doTraversal()來從新繪製界面。經過上面的分析咱們調用invalidate等刷新操做時,系統並不會當即刷新界面,而是等到Vsync消息後纔會刷新頁面。
咱們這邊已經有了前輩給出的結論,咱們知道了界面刷新(requestLayout或者invalidate)的過程必定會觸發scheduleTraversals()方法,這說明會添加同步屏障消息,那確定有移除同步屏障消息的步驟,這個步驟頗有可能存在doTraversal()方法中,來看下這個方法
void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } performTraversals(); if (mProfile) { Debug.stopMethodTracing(); mProfile = false; } } }
doTraversal()是怎麼被調用呢?
調用:mTraversalRunnable在scheduleTraversals()中使用了
final TraversalRunnable mTraversalRunnable = new TraversalRunnable(); void scheduleTraversals() { if (!mTraversalScheduled) { ... mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); ... }
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
postCallback是Choreographer類中方法,該類涉及巨多的消息傳遞,並且都是使用了異步消息setAsynchronous(true)
,這些都是和界面刷新相關,因此都是優先處理,完整的流程能夠看上面貼的文章
postCallback的核心就是讓DisplayEventReceiver註冊了個Vsync的通知,後期收到送來的Vsync後,咱們就執行doTraversal()來從新繪製界面
調用View的requestLayout或者invalidate時,最終都會執行scheduleTraversals(),此時會在主線程消息隊列中插入一個同步屏障消息(中止全部同步消息分發),會將mTraversalRunnable添加到mCallbackQueues中,並註冊接收Vsync的監聽,當接受到Vsync通知後,會發送一個異步消息,觸發遍歷執行mCallbackQueues的方法,這會執行咱們添加的回調mTraversalRunnable,從而執行doTraversal(),此時會移除主線程消息隊列中同步屏障消息,最後執行繪製操做
調用requestLayout或者invalidate時,會在主線程消息隊列中插入一個同步屏障消息,同時註冊接收Vsync的監聽;當接受到Vsync通知,會發送一個異步消息,執行真正的繪製事件:此時會移除消息隊列中的同步屏障消息,而後纔會執行繪製操做
關於Vsync
來看下執行同步消息時間片:這圖真吉兒很差畫,吐血
相關總結
同步消息和異步消息使用建議
在正常的狀況,確定不建議使用異步消息,此處假設一個場景:由於某種需求,你發送了大量的異步消息,因爲消息進入消息隊列的特殊性,系統發送的異步消息,也只能乖乖的排在你的異步消息後面,假設你的異步消息佔據了大量的時間片,甚至佔用了幾幀,致使系統UI刷新的異步消息沒法被及時執行,此時頗有可能發生掉幀
固然,若是你能看明白這個同步屏障欄目所寫的東西,相信何時設置消息爲異步,心中確定有數
上面源碼基本就分析到這邊了,我們看看能根據這些知識點,能提一些什麼問題呢?
一、先來個本身想的問題:Handler中主線程的消息隊列是否有數量上限?爲何?
這問題整的有點雞賊,可能會讓你想到,是否有上限這方面?而不是直接想到到上限數量是多少?
解答:Handler主線程的消息隊列確定是有上限的,每一個線程只能實例化一個Looper實例(上面講了,Looper.prepare只能使用一次),否則會拋異常,消息隊列是存在Looper()中的,且僅維護一個消息隊列
重點:每一個線程只能實例化一次Looper()實例、消息隊列存在Looper中
拓展:MessageQueue類,其實都是在維護mMessage,只須要維護這個頭結點,就能維護整個消息鏈表
二、Handler中有Loop死循環,爲何沒有卡死?爲何沒有發生ANR?
先說下ANR:5秒內沒法響應屏幕觸摸事件或鍵盤輸入事件;廣播的onReceive()
函數時10秒沒有處理完成;前臺服務20秒內,後臺服務在200秒內沒有執行完畢;ContentProvider的publish在10s內沒進行完。因此大體上Loop死循環和ANR聯繫不大,問了個正確的廢話,因此觸發事件後,耗時操做仍是要放在子線程處理,handler將數據通信到主線程,進行相關處理。
線程實質上是一段可運行的代碼片,運行完以後,線程就會自動銷燬。固然,咱們確定不但願主線程被over,因此整一個死循環讓線程保活。
爲何沒被卡死:在事件分發裏面分析了,在獲取消息的next()方法中,若是沒有消息,會觸發nativePollOnce方法進入線程休眠狀態,釋放CPU資源,MessageQueue中有個原生方法nativeWake方法,能夠解除nativePollOnce的休眠狀態,ok,我們在這倆個方法的基礎上來給出答案
當消息隊列中消息爲空時,觸發MessageQueue中的nativePollOnce方法,線程休眠,釋放CPU資源
消息插入消息隊列,會觸發nativeWake喚醒方法,解除主線程的休眠狀態
綜上:消息隊列爲空,會阻塞主線程,釋放資源;消息隊列爲空,插入消息時候,會觸發喚醒機制
本質上,主線程的運行,總體上都是以事件(Message)爲驅動的
三、爲何不建議在子線程中更新UI?
多線程操做,在UI的繪製方法表示這不安全,不穩定。
假設一種場景:我會須要對一個圓進行改變,A線程將圓增大倆倍,B改變圓顏色。A線程增長了圓三分之一體積的時候,B線程此時,讀取了圓此時的數據,進行改變顏色的操做;最後的結果,可能會致使,大小顏色都不對。。。
四、可讓本身發送的消息優先被執行嗎?原理是什麼?
這個問題,我感受只能說:在有同步屏障的狀況下是能夠的。
同步屏障做用:在含有同步屏障的消息隊列,會及時的屏蔽消息隊列中全部同步消息的分發,放行異步消息的分發。
在含有同步屏障的狀況,我能夠將本身的消息設置爲異步消息,能夠起到優先被執行的效果。
五、子線程和子線程使用Handler進行通訊,存在什麼弊端?
子線程和子線程使用Handler通訊,某個接受消息的子線程確定使用實例化handler,確定會有Looper操做,Looper.loop()內部含有一個死循環,會致使線程的代碼塊沒法被執行完,該線程始終存在。
若是在完成通訊操做,咱們通常可使用: mHandler.getLooper().quit() 來結束分發操做
六、Handler中的阻塞喚醒機制?
這個阻塞喚醒機制是基於 Linux 的 I/O 多路複用機制 epoll 實現的,它能夠同時監控多個文件描述符,當某個文件描述符就緒時,會通知對應程序進行讀/寫操做.
MessageQueue 建立時會調用到 nativeInit,建立新的 epoll 描述符,而後進行一些初始化並監聽相應的文件描述符,調用了epoll_wait方法後,會進入阻塞狀態;nativeWake觸發對操做符的 write
方法,監聽該操做符被回調,結束阻塞狀態
詳細請查看:同步屏障?阻塞喚醒?和我一塊兒重讀 Handler 源碼
七、什麼是IdleHandler?什麼條件下觸發IdleHandler?
IdleHandler的本質就是接口,爲了在消息分發空閒的時候,能處理一些事情而設計出來的
具體條件:消息隊列爲空的時候、發送延時消息的時候
八、消息處理完後,是直接銷燬嗎?仍是被回收?若是被回收,有最大容量嗎?
Handler存在消息池的概念,處理完的消息會被重置數據,採用頭插法進入消息池,取的話也直接取頭結點,這樣會節省時間
消息池最大容量爲50,達到最大容量後,再也不接受消息進入
九、不當的使用Handler,爲何會出現內存泄漏?怎麼解決?
先說明下,Looper對象在主線程中,整個生命週期都是存在的,MessageQueue是在Looper對象中,也就是消息隊列也是存在在整個主線程中;咱們知道Message是須要持有Handler實例的,Handler又是和Activity存在強引用關係
存在某種場景:咱們關閉當前Activity的時候,當前Activity發送的Message,在消息隊列還未被處理,Looper間接持有當前activity引用,由於倆者直接是強引用,沒法斷開,會致使當前Activity沒法被回收
思路:斷開倆者之間的引用、處理完分發的消息,消息被處理後,之間的引用會被重置斷開
解決:使用靜態內部類弱引Activity、清空消息隊列
寫這篇文章加上思惟導圖,也大概整了十三來張圖,我真的盡力了!