Handler是什么
-
先來看官方文檔對(duì)Handler的描述
A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
Handler可以讓我們發(fā)送和處理和線程的
MessageQueue
關(guān)聯(lián)的Message
和Runnable
對(duì)象籽孙。每個(gè)Handler實(shí)例都與一個(gè)線程和該線程的消息隊(duì)列相關(guān)聯(lián)晓折。當(dāng)創(chuàng)建一個(gè)Handler時(shí)往扔,Handler就會(huì)被綁定到創(chuàng)建它的線程/消息隊(duì)列谆级。創(chuàng)建之后赞辩,Handler就可以發(fā)送消息(messages) 和任務(wù)(runnables)到該消息隊(duì)列昔案,并且在消息出隊(duì)的時(shí)候執(zhí)行它們烁挟。所以Handler其實(shí)是Android為我們提供的一套消息機(jī)制净蚤。
Handler作用
-
同樣看看官方文檔中的描述
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed at some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
可以看到Handler主要有兩個(gè)作用:
- 讓消息(messages) 和任務(wù)(runnables)在將來某個(gè)時(shí)刻執(zhí)行
- 將要在不同于自己線程上執(zhí)行的操作(messages和runnables)插入隊(duì)列
第一個(gè)作用很好理解,也就是說可以通過Handler延時(shí)執(zhí)行一些操作共屈;第二個(gè)怎么來理解呢绑谣?前面有提過,每個(gè)Handler都會(huì)和一個(gè)線程/消息隊(duì)列綁定起來拗引。那么通過該Handler發(fā)送消息和任務(wù)就會(huì)到該線程的消息隊(duì)列中并且執(zhí)行借宵。所以可以通過Handler將一些操作放到其它線程中去執(zhí)行。舉個(gè)例子矾削,Android是不允許在子線程中更新UI的壤玫,所以在子線程中可以通過Handler將更新UI的操作放到主線程/UI線程中執(zhí)行
-
下面這一段描述其實(shí)就為第二個(gè)作用進(jìn)行了說明,并且我們知道了在主線程中會(huì)自動(dòng)維護(hù)一個(gè)消息隊(duì)列用于維護(hù)
When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing the top-level application objects (activities, broadcast receivers, etc) and any windows they create. You can create your own threads, and communicate back with the main application thread through a Handler.
為應(yīng)用程序創(chuàng)建進(jìn)程的時(shí)候怔软,主線程也就是ActivityThread會(huì)專門啟動(dòng)一個(gè)消息隊(duì)列對(duì)于管理頂級(jí)的應(yīng)用程序?qū)ο罂严福热鏏ctivity、BroadcastReceiver等等以及它們創(chuàng)建的任何窗口挡逼。可以創(chuàng)建自己的線程括改,并通過Handler與主應(yīng)用程序線程進(jìn)行通信。
Handler的基本使用
-
Handler的最簡(jiǎn)單的使用方式如下:
- 創(chuàng)建Handler
//創(chuàng)建Handler private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what){ //do sth } } };
- 發(fā)送empty消息
int what = 1; mHandler.sendEmptyMessage(what);
- 攜帶對(duì)象
class Person { String name; int age; public Person(String name, int age) { this.name = name; this.age = age; } } Message message =mHandler.obtainMessage(); message.what = 1; message.obj = new Person("ygg",25); mHandler.sendMessage(message);
- 發(fā)送延時(shí)消息
mHandler.sendMessageDelayed(message,1000);
- 發(fā)送runnable
Runnable runnable = new Runnable() { @Override public void run() { //do sth } }; mHandler.postDelayed(runnable,1000);
- 移除message和runnable
int what = 1; mHandler.removeMessages(what); mHandler.removeCallbacks(runnable); //移除所有消息和任務(wù) mHandler.removeCallbacksAndMessages(null);
- 攔截消息
private Handler mHandler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { if (msg.what == 1){ //do sth return true; } return false; } }) { @Override public void handleMessage(Message msg) { switch (msg.what){ //do sth } } };
在創(chuàng)建的時(shí)候可以傳入一個(gè)
Callback
對(duì)消息進(jìn)行一個(gè)攔截,Callback
中有一個(gè)具有返回值的handleMessage
方法嘱能,返回true
表示需要攔截該消息吝梅,攔截后下面返回值為void
的handleMessage
方法不會(huì)再被調(diào)用- 指定Looper
private Handler mHandler = new Handler(Looper.getMainLooper());
關(guān)于
Looper
,會(huì)在后面進(jìn)行講解惹骂,在這我們先知道可以為Handler 指定Looper
苏携,這也是在非UI線程中和UI線程交互的實(shí)現(xiàn)方式,將Looper
指定為UI線程的Looper
对粪,發(fā)送的消息就會(huì)到UI線程中處理- 其它使用方式
在子線程中更新UI右冻,我們常常使用runOnUiThread(new Runnable() { @Override public void run() { } }); //Activity.class public final void runOnUiThread(Runnable action) { if (Thread.currentThread() != mUiThread) { mHandler.post(action); } else { action.run(); } }
runOnUiThread
,可以看到實(shí)際上它內(nèi)部也是使用Handler實(shí)現(xiàn)的,如果不是在UI 線程就交給Handler處理著拭,否則直接執(zhí)行
我們常在private View view; view.post(new Runnable() { @Override public void run() { } }); //View.class public boolean post(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { return attachInfo.mHandler.post(action); } // Postpone the runnable until we know on which thread it needs to run. // Assume that the runnable will be successfully placed after attach. getRunQueue().post(action); return true; }
Activity#onCreate
中用View.post
方式纱扭,在里面獲取控件的寬高等操作,內(nèi)部實(shí)際上也是利用Handler實(shí)現(xiàn)的儡遮,需要注意在Activity#onCreate
時(shí)這里的AttachInfo是為空的乳蛾,所以走的是下面的getRunQueue().post(action);
,它會(huì)先把Runnable用數(shù)組存起來,等到第一次測(cè)量完成后才執(zhí)行鄙币,所以我們可以用該方法獲取到寬高肃叶。關(guān)于View.post
,想了解更多信息的話可以關(guān)注博主十嘿,之后會(huì)寫一篇文章進(jìn)行分析因惭。
-
需要注意的點(diǎn)
- 對(duì)于Message的創(chuàng)建使用
Handler.obtainMessage()
而不是new Message()
先來看看obtainMessage
方法
//Handler.class public final Message obtainMessage() { return Message.obtain(this); } //Message .class public static Message obtain(Handler h) { Message m = obtain(); m.target = h; return m; } /** * Return a new Message instance from the global pool. Allows us to * avoid allocating new objects in many cases. */ 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(); }
可以看到
obtainMessage
方法會(huì)從一個(gè)公共緩存池返回一個(gè)Message實(shí)例,避免頻繁的分配對(duì)象绩衷。所以使用該方法可以對(duì)Message對(duì)象進(jìn)行復(fù)用筛欢,從而減少M(fèi)essage對(duì)象大量的創(chuàng)建。- Handler的創(chuàng)建應(yīng)該改為靜態(tài)內(nèi)部類唇聘,在Activity關(guān)閉的時(shí)候應(yīng)該調(diào)用``清空消息和任務(wù),如果需要在Handler內(nèi)部使用Activity柱搜,采用弱引用方式迟郎,避免內(nèi)存泄漏
private Handler mHandler = new MyHandler(this); static class MyHandler extends Handler{ WeakReference<Activity> mWeakReference; public MyHandler(Activity activity) { mWeakReference = new WeakReference<Activity>(activity); } @Override public void handleMessage(Message msg){ final Activity activity = mWeakReference.get(); if(activity!=null){ // do sth } } } @Override protected void onDestroy() { super.onDestroy(); mHandler.removeCallbacksAndMessages(null); }
在Java 中,非靜態(tài)的內(nèi)部類和匿名內(nèi)部類都會(huì)隱式地持有其外部類的引用聪蘸,靜態(tài)的內(nèi)部類不會(huì)持有外部類的引用宪肖。如果發(fā)送了一個(gè)延時(shí)Message,此時(shí)MessageQueue就會(huì)持有一個(gè)Message對(duì)象健爬。再來看發(fā)送消息是如何處理
//Handler.class public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 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); }
可以看到最終會(huì)調(diào)用
enqueueMessage
方法入隊(duì)控乾,此時(shí)MessageQueue 便會(huì)持有一個(gè)Message對(duì)象,關(guān)注這行代碼:msg.target = this;
,將Handler對(duì)象賦值給Message的target變量娜遵,所以Message會(huì)持有Handler對(duì)象蜕衡,如果Handler是非靜態(tài)內(nèi)部類的話,它又持有Activity,最終就會(huì)形成MessageQueue ->Message->Handler->Activity
這樣一條引用鏈设拟,當(dāng)Activity退出時(shí)還有消息沒有被執(zhí)行就會(huì)導(dǎo)致Activity不會(huì)被回收慨仿,最終導(dǎo)致Activity泄漏久脯。- 在子線程中創(chuàng)建Handler時(shí)必須指定
Looper
或者調(diào)用Looper.prepare()
和Looper.loop()
為線程創(chuàng)建一個(gè)Looper并啟動(dòng)Looper
new Thread(new Runnable() { //指定Looper private Handler mHandler = new Handler(Looper.getMainLooper()); @Override public void run() { //先為線程創(chuàng)建一個(gè)Looper,并啟動(dòng) Looper.prepare(); Looper.loop(); Handler mHandler1 = new Handler(); } }).start();
從下面的源碼中可以看到在子線程中創(chuàng)建Handler,會(huì)對(duì)Looper進(jìn)行一個(gè)檢測(cè)镰吆,如果為空的話帘撰,會(huì)報(bào)錯(cuò)。至于為什么需要調(diào)用
Looper.prepare()
万皿,在了解了Handler摧找、Looper、MessageQueue牢硅、Message之間的關(guān)系之后就可以解釋了蹬耘。public Handler() { this(null, false); } public Handler(Callback callback, boolean async) { //建議創(chuàng)建成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()); } } //檢測(cè)有沒有設(shè)置Looper 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; }
- 對(duì)于Message的創(chuàng)建使用
Handler、Looper唤衫、MessageQueue婆赠、Message之間的關(guān)系
- Handler與Message之間的關(guān)系
首先Message
是一個(gè)Parcelable對(duì)象,作為消息的載體佳励。主要注意target
休里、callback
、next
赃承。
1.public final class Message implements Parcelable { public int what; public int arg1; public int arg2; public Object obj; Handler target; Runnable callback; Message next; //是否是異步消息 public boolean isAsynchronous() { return (flags & FLAG_ASYNCHRONOUS) != 0; } //設(shè)置消息是否是異步的妙黍,異步則意味著它不受 Looper同步障礙影響。 public void setAsynchronous(boolean async) { if (async) { flags |= FLAG_ASYNCHRONOUS; } else { flags &= ~FLAG_ASYNCHRONOUS; } } }
Handler target
注意到它是Handler類型瞧剖,還記得前面Handler是如何發(fā)送一個(gè)消息的嗎拭嫁?最終會(huì)調(diào)用
關(guān)注private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
msg.target = this;
,也就是說當(dāng)Handler發(fā)送一個(gè)Message
的時(shí)候抓于,Message
會(huì)將發(fā)送它的Handler記錄下來做粤。這也就是它們之間的關(guān)系:Message會(huì)持有發(fā)送它的Handler對(duì)象。最終是為了在該Handler的handleMessage
處理該消息
2.Runnable callback
callback是一個(gè)Runnable
對(duì)象捉撮,還記得Handler可以直接發(fā)送一個(gè)Runnable嗎怕品?
實(shí)際上也是將public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
Runnable
對(duì)象包裝成一個(gè)Message
,而Message
的callback就是發(fā)送的Runnable
對(duì)象。先記住這一點(diǎn)巾遭,在消息分發(fā)的時(shí)候會(huì)用到肉康。-
Message next
next也是一個(gè)Message
類型,相信大家都有見到過這種寫法——鏈表灼舍。實(shí)際上MessageQueue
是一個(gè)單鏈表形式的隊(duì)列
-
-
ThreadLocal簡(jiǎn)介
為了便于理解線程與Looper
以及MessageQueue
之間的關(guān)系吼和,在這里對(duì)ThreadLocal
做一個(gè)簡(jiǎn)要的介紹。ThreadLocal
可以提供一個(gè)線程的局部變量骑素。每一個(gè)線程通過ThreadLocal#set
保存和ThreadLocal#get
訪問的數(shù)據(jù)都有它們自己的副本炫乓。也就是說通過ThreadLocal
保存的數(shù)據(jù)是線程隔離的。舉個(gè)例子static final ThreadLocal<Integer> sThreadLocal = new ThreadLocal<>(); new Thread(new Runnable() { @Override public void run() { sThreadLocal.set(1); try { Thread.sleep(2000); sThreadLocal.get();//1 } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { @Override public void run() { sThreadLocal.set(2); new Thread(new Runnable() { @Override public void run() { sThreadLocal.set(3); sThreadLocal.get();//3 } }).start(); try { Thread.sleep(2000); sThreadLocal.get(); //2 } catch (InterruptedException e) { e.printStackTrace(); } } }).start();
事例非常簡(jiǎn)單。在每個(gè)線程中獲取的都會(huì)是該線程本身保存的值厢岂。
-
接下來看一下
ThreadLocal#set
和ThreadLocal#get
方法光督,幫助我們理解這個(gè)過程//ThreadLocal.class public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); } ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
ThreadLocalMap
是ThreadLocal
的一個(gè)內(nèi)部類,在這里我們不深入講它是如何插入和獲取值的塔粒。就當(dāng)它是一個(gè)容器结借。比如HashMap之類的,可以用來保存插入的內(nèi)容卒茬。我們可以看到整個(gè)流程其實(shí)非常的清晰- 調(diào)用
ThreadLocal.set
實(shí)際上會(huì)為當(dāng)前線程創(chuàng)建一個(gè)容器(ThreadLocalMap)船老,也就是t.threadLocals
,最終保存的內(nèi)容是放在這個(gè)容器當(dāng)中的 - 調(diào)用
ThreadLocal.get
實(shí)際上是從當(dāng)前線程取出之前創(chuàng)建的容器(ThreadLocalMap),然后從這個(gè)容器中獲取對(duì)應(yīng)的內(nèi)容圃酵。 - 也就是說實(shí)際上每一個(gè)線程都會(huì)有一個(gè)自己的容器(ThreadLocalMap)柳畔,那么保存在里面的內(nèi)容肯定是它自己獨(dú)享的,和其它線程之間不會(huì)互相干擾郭赐。需要注意在線程退出(exit)的時(shí)候會(huì)自動(dòng)釋放該容器薪韩。
- 調(diào)用
-
Looper、MessageQueue之間的關(guān)系
- MessageQueue 簡(jiǎn)介
public final class MessageQueue { //Native消息機(jī)制中MessageQueue的地址 private long mPtr; // used by native code //處理的消息 Message mMessages; //可以添加一個(gè)IdleHandler捌锭,用于在每次空閑(沒有消息處理)時(shí)回調(diào)俘陷,可以利用該特性進(jìn)行一些優(yōu)化處理一些需要在主線程執(zhí)行又比較耗時(shí)操作,在系統(tǒng)空閑時(shí)才執(zhí)行 private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>(); private IdleHandler[] mPendingIdleHandlers; // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout. //用來判斷 next()方法是否因?yàn)檎{(diào)用pollOnce() 在阻塞 private boolean mBlocked; private native static long nativeInit(); //阻塞調(diào)用該方法的線程观谦,類似于Object.wait拉盾,不過會(huì)在阻塞timeoutMillis之后喚醒線程 private native void nativePollOnce(long ptr, int timeoutMillis); //喚醒線程以繼續(xù)執(zhí)行 private native static void nativeWake(long ptr); MessageQueue(boolean quitAllowed) { //是否可以停止 mQuitAllowed = quitAllowed; mPtr = nativeInit(); } //消息入隊(duì) boolean enqueueMessage(Message msg, long when) { synchronized (this) { msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // 新消息入隊(duì),隊(duì)列中沒有消息或者新消息不需要延時(shí)豁状,或者新消息的執(zhí)行時(shí)間比當(dāng)前隊(duì)列中最早執(zhí)行的消息執(zhí)行時(shí)間還要早捉偏,插入頭部 msg.next = p; mMessages = msg; //如果當(dāng)前正在阻塞即mBlocked == true,則喚醒線程 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. //判斷喚醒條件泻红,當(dāng)前消息隊(duì)列頭部消息是屏障消息(也就是正在阻塞)夭禽,且當(dāng)前插入的消息為異步消息,并且當(dāng)前沒有需要處理的消息 needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; //循環(huán)找到需要插入的地方插入該消息谊路,延時(shí)越長(zhǎng)排在越后面 for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } //入隊(duì) msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { //喚醒驻粟,結(jié)束等待,繼續(xù)執(zhí)行 nativeWake(mPtr); } } return true; } //消息出隊(duì) Message next() { final long ptr = mPtr; if (ptr == 0) { return null; } ... int nextPollTimeoutMillis = 0; for (;;) { //阻塞nextPollTimeoutMillis時(shí)間凶异,第一次是0,所以會(huì)往下執(zhí)行 nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { //獲取系統(tǒng)開機(jī)到現(xiàn)在的時(shí)間 final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; //msg.target == null表示是屏障信息挤巡,則需要找到隊(duì)列中下一個(gè)異步消息執(zhí)行 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) { //比較時(shí)間剩彬,還沒有到要執(zhí)行的時(shí)間,需要等待 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. // 不需要等待或者等待時(shí)間到了矿卑,直接返回該消息 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. //沒有更多的消息要處理喉恋,-1表示一直阻塞 nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. //結(jié)束直接返回空 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. //沒有消息的第一時(shí)間,獲取IdleHandler的個(gè)數(shù) if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } //沒有設(shè)置IdleHandler,繼續(xù)阻塞 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 //執(zhí)行IdleHandler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } //重置個(gè)數(shù)轻黑,只執(zhí)行一次 // 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. //執(zhí)行完IdleHandler后糊肤,看看有沒有消息需要執(zhí)行 nextPollTimeoutMillis = 0; } } //向Looper的消息隊(duì)列發(fā)布同步障礙。 //消息機(jī)制還是在執(zhí)行氓鄙,但是遇到屏障時(shí)馆揉,隊(duì)列中的后續(xù)同步消息將被暫停(阻止執(zhí)行),直到通過調(diào)用removeSyncBarrier并指定標(biāo)識(shí)同步屏障的令牌來釋放屏障 public int postSyncBarrier() { return postSyncBarrier(SystemClock.uptimeMillis()); } }
MessageQueue
是一個(gè)單項(xiàng)鏈表實(shí)現(xiàn)的隊(duì)列抖拦∩ǎ可以看到MessageQueue
在消息入隊(duì)的時(shí)候,會(huì)為Message
選擇一個(gè)合適的位置插入态罪,延時(shí)時(shí)間越小噩茄,就排在越前面,當(dāng)下一次被喚醒時(shí)复颈。排在前面的就會(huì)先被取出執(zhí)行绩聘。
需要注意消息出隊(duì)是會(huì)阻塞的。獲取下一個(gè)消息耗啦,如果下一個(gè)消息是可以執(zhí)行的凿菩,也就是now >=msg.when,就直接返回芹彬,如果是延時(shí)消息蓄髓,則阻塞起來,到達(dá)延時(shí)時(shí)間就會(huì)被喚醒舒帮,從而繼續(xù)取出消息執(zhí)行会喝。因?yàn)槿腙?duì)時(shí)經(jīng)過排序,如果隊(duì)頭消息都需要等待玩郊,后面的消息肯定也是需要等待的肢执。這也是延時(shí)消息的實(shí)現(xiàn)原理。- 解決前面的問題:為什么需要在子線程中調(diào)用
Looper.prepare()
译红? 先來看看該方法做了什么
//Looper.class static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); 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)); } public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
我們可以看到有個(gè)拋出異常预茄,也就是說一個(gè)線程只能創(chuàng)建一個(gè)Looper,也就是只能調(diào)用一次
Looper.prepare()
。該方法實(shí)際上就是創(chuàng)建了一個(gè)Looper
對(duì)象侦厚,并且保存到ThreadLocal
中耻陕。利用ThreadLocal
,實(shí)現(xiàn)Looper
的線程隔離刨沦。默認(rèn)情況下诗宣,非UI線程的Looper
為空,所以在子線程中創(chuàng)建Handler時(shí)檢測(cè)拋出異常想诅,需要先調(diào)用Looper.prepare()
方法private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
在創(chuàng)建
Looper
的時(shí)候就會(huì)創(chuàng)建一個(gè)MessageQueue
與其綁定起來召庞。- Looper和MessageQueue的關(guān)聯(lián)
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static void loop() { //返回sThreadLocal存儲(chǔ)的Looper實(shí)例 final Looper me = myLooper(); //loop方法必須在prepare方法之后運(yùn)行 if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } //獲取Looper實(shí)例中的消息隊(duì)列mQueue final MessageQueue queue = me.mQueue; ... for (;;) { //next()方法用于取出消息隊(duì)列中的消息岛心,如果沒有符合的消息或者沒有消息,則線程阻塞 Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } //時(shí)間分發(fā)前打印時(shí)間 // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } //把消息派發(fā)給msg的target屬性篮灼,然后用dispatchMessage方法去處理 msg.target.dispatchMessage(msg); //消息執(zhí)行完后打印時(shí)間 if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } msg.recycleUnchecked(); } }
可以看到
loop()
方法就是開啟一個(gè)循環(huán)忘古,一直從消息隊(duì)列中取出消息,并交給Handler#dispatchMessage
方法進(jìn)行分發(fā)處理诅诱。在這額外說一下髓堪,在消息分發(fā)前后都調(diào)用了Looper 的Printer 打印了時(shí)間。而BlockCanary正是利用這一點(diǎn)監(jiān)聽卡頓的逢艘。下面看看Handler是如何分發(fā)消息的 -
Handler分發(fā)消息
//Handler.class public void dispatchMessage(Message msg) { if (msg.callback != null) { //如果msg.callback不為空旦袋,實(shí)際上就是調(diào)用的是post方法,傳入了一個(gè)Runnable對(duì)象它改,直接執(zhí)行 handleCallback(msg); } else { if (mCallback != null) { //如果創(chuàng)建Handler的時(shí)候傳入了Callback 疤孕,則先調(diào)用Callback#handleMessage方法先處理消息 if (mCallback.handleMessage(msg)) { //如果Callback#handleMessage方法返回true,則攔截 return; } } //執(zhí)行handleMessage方法 handleMessage(msg); } } private static void handleCallback(Message message) { //執(zhí)行Runnable的run方法 message.callback.run(); }
Handler分發(fā)消息比較簡(jiǎn)單央拖,先判斷Message 的callback是不是為空祭阀,也就是判斷是不是調(diào)用的post方法,注意post方法傳入的Runnable也是包裝成Message然后入隊(duì)的鲜戒,所以消息延時(shí)一樣適用专控。如果是Runnable,直接執(zhí)行遏餐。否則先判斷Handler的Callback是否為空伦腐,先執(zhí)行
Handler#Callback#handleMessage
方法,所以我們可以通過傳入一個(gè)Callback對(duì)消息進(jìn)行攔截失都,最后才執(zhí)行Handler#handleMessage
方法柏蘑。
Handler消息機(jī)制流程
-
Looper.prepare()
為每個(gè)線程創(chuàng)建了一個(gè)Looper,每個(gè)Looper會(huì)創(chuàng)建一個(gè)MessageQueue,用于存放Message -
Looper.loop()
開啟一個(gè)循環(huán)粹庞,不斷的從MessageQueue中取出Message并且交由Handler進(jìn)行分發(fā)處理 -
new Handler()
會(huì)從線程中取出與線程綁定的Looper咳焚,再?gòu)腖ooper中拿到MessageQueue -
Handler.sendXXX()
一系列發(fā)送消息的方法最終會(huì)通過enqueueMessage
方法將Message插入MessageQueue中,此時(shí)Message的target屬性綁定為發(fā)送它的Handler庞溜,最后消息出隊(duì)時(shí)通過Message.target.dispatchMessage
方法將Message交回給發(fā)送該Message的Handler進(jìn)行處理革半,最終回到handleMessage.handleMessage
方法
為什么非UI線程不能直接更新UI,只能通過Handler機(jī)制更新UI
-
為什么非UI線程直接更新UI會(huì)報(bào)錯(cuò)
//ViewRootImpl.class public ViewRootImpl(Context context, Display display) { ... mThread = Thread.currentThread(); } @Override public void invalidateChild(View child, Rect dirty) { invalidateChildInParent(null, dirty); } @Override public ViewParent invalidateChildInParent(int[] location, Rect dirty) { checkThread(); ... } void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } }
View的繪制都是通過
ViewRootImpl
操作的流码,關(guān)于View的繪制流程又官,可以關(guān)注博主,后續(xù)也會(huì)寫一篇文章漫试。ViewRootImpl
是在UI線程創(chuàng)建的六敬。我們可以看到每次更新UI都會(huì)檢測(cè)是不是主線程/UI線程。當(dāng)然在Activity#onCreate
之前直接在子線程/非UI線程直接更新UI也是可以的商虐,因?yàn)榇藭r(shí)ViewRootImpl并沒有被創(chuàng)建觉阅,它是在Activity#onResume
中創(chuàng)建的。不過正常來說在這個(gè)時(shí)候更新UI并沒有什么實(shí)際的作用秘车。 -
為什么只能通過Handler機(jī)制更新UI
- 是因?yàn)榭紤]到多線程并發(fā)更新UI的問題典勇。不知道大家有沒有想過這樣一個(gè)問題:如果非UI線程能夠直接更新UI,那么在有多個(gè)非UI線程同時(shí)更新UI會(huì)發(fā)生什么問題叮趴?
- 答案是會(huì)導(dǎo)致界面UI更新錯(cuò)亂割笙。我們都知道,多線程的情況下會(huì)存在一個(gè)并發(fā)的問題眯亦,在這種情況下更新UI就可能造成預(yù)期之外的結(jié)果伤溉。那么怎么處理這個(gè)問題呢?
- 處理多線程問題妻率,一般我們會(huì)考慮加鎖乱顾,如果對(duì)更新UI的操作加鎖,會(huì)帶來性能上的問題宫静。更新UI的操作在Android中是非常頻繁的操作走净。如果加鎖了,線程對(duì)鎖的獲取和釋放孤里、線程的切換都會(huì)帶來資源的消耗伏伯,而對(duì)于更新UI的操作,一旦多花了點(diǎn)時(shí)間捌袜,都有可能導(dǎo)致界面的卡頓等不好的結(jié)果说搅。Android 16.6ms內(nèi)要完成一次刷新操作想必都有聽說過。所以加鎖是不適合的虏等。那么應(yīng)該如何處理呢弄唧?
- 答案就是通過Handler機(jī)制更新UI,非UI線程將所有更新UI的操作都放到主線程/UI線程中博其,這些更新UI的操作就會(huì)被放到隊(duì)列中一個(gè)接一個(gè)被執(zhí)行套才,即解決了多線程并發(fā)的問題,也避免了加鎖帶來的性能上的消耗慕淡。