知識的獲取須要知其一,還要知其二,有機會還要知其三啦。java
在Android中常常會使用到Handler,或者在View的更新中常常能見到Handler的代碼,只有主線程才能操做UI這是咱們的固定思惟。咱們一般在子線程作完耗時操做後,通知主線程去更新UI,通常都是經過Handler發送消息,主線程接收消息後執行對應的邏輯。android
那麼不妨問問幾個問題:markdown
若是能一下說明白,大佬請慢些走,手動奉杯茶【滑稽】。網絡
handler是android給咱們提供用來更新UI的一套機制,也是一套消息處理機制(這是重點),咱們能夠發送消息,也能夠經過它處理消息。在系統的源碼中,activity也會經過Handler機制發送消息回調生命週期函數。併發
咱們常常會在子線程去處理大量運算、網絡請求、圖片壓縮等操做。當咱們操做完,要通知主線程操做結果,能夠進行下一步行動了。這時候又有一個問題,若是幾個線程同時去通知主線程呢,那麼必然會有併發問題,咱們怎麼避免這個問題又不會影響性能呢?(加同步鎖會影響性能)函數
Google大哥仍是瞭解開發者意願的,搬出來了Handler機制,內部有Looper、MessageQueue兩個重要角色,光看名字咱們就知道這是一個消息隊列,經過隊列方式去對UI進行更新。很好解決了多個子線程的併發問題,還不影響性能。oop
第一步確定是從new Handler()開始了。Looper、MessageQueue、CallBack,能夠看到咱們熟悉的3個對象都有了。post
在經過Looper.myLooper得到looper對象時,若是Looper爲空,那麼會拋出異常(提示:不能在未調用Looper.prepare()的線程裏建立Handler)。在Android的主線程中,啓動時便已調用過,能夠直接建立Handler,可是在其它子線程裏,直接建立Handler是會致使應用崩潰的。性能
接着從Looper中獲取了MessageQueue對象了,存儲消息都是保存在這裏面。ui
進入myLooper方法,能夠看到對象是經過ThreadLocal獲取的,ThreadLocal是一個線程局部變量(map結構),和普通變量的不一樣在於,每一個線程持有這個變量的一個副本,能夠獨立調用set和get方法,而且線程之間不會發生衝突(避免併發)。
/** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static @Nullable Looper myLooper() { return sThreadLocal.get(); } 複製代碼
既然知道了ThreadLocal是什麼,那麼就去看看在哪調用它的set方法。
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.prepare()建立了一個Looper對象,而後將對象存入到了sThreadLocal線程局部變量中。一樣在一個線程中重複調用了prepare()方法,發現sThreadLocal已經不爲空了,是會拋出異常的,提示「一個線程中只能有一個Looper對象」。
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } 複製代碼
這說明建立的Looper都是獨立且惟一的,到這裏Looper的建立過程也就完畢了。
handler.sendEmptyMessage()發送一個空消息,實際上幾個發送消息方法最終調用的都是sendMessageAtTime()方法,而後執行queue.enqueueMessage()將消息存入隊列中(注意看,target默認指向的是Handler本身)。
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; //target指向的是Handler本身 if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); } 複製代碼
接下來再看看消息的發送,咱們知道發送是調用的Looper.loop()方法,直接進入源碼。
看到先獲取並判斷Looper對象是否爲空,而後拿到MessageQueue消息隊列。在下面定義了一個死循化,不停的從隊列中取值,若是爲空則return掉,不爲空則執行msg.target.dispatchMessage(msg)。敲黑板了!!!,這裏的target以前說過默認狀況是Handler,將信息又丟給Handler本身處理。
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 (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } //....省略一大堆代碼 try { msg.target.dispatchMessage(msg); dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } //...再省略一大堆代碼 msg.recycleUnchecked(); } } 複製代碼
在這裏咱們就介紹完了Message的存儲與發送,在Handler本身去處理消息時,會判斷callback是否爲空(去看看post(runnable)),爲空則執行handleMessage(msg)方法,讓咱們本身跟本身去完了。
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } 複製代碼
彩蛋來了,來一篇圖解Handler吧。
我(Handler)對十元(Looper)說:「我喜歡你,作我女友吧(發送消息給Looper)」,十元接收到後確定不能立馬就答應我啊,女孩子要矜持嘛,要想想:「我要不要接受他,他之後還會不會對我好,我不接受他他會不會很傷心,萬一這帥哥跳河了怎麼辦(Looper.loop)」。而後過了一會想通了,以爲嗯能夠的,回答我:「好吧(Looper分發消息給Handler)」。這回到我了,我接收到消息後確定不是結束啊,還要想着兩人的將來的,如今的年輕人又不是肯定關係就能結婚的,固然是選擇繼續關愛、呵護她呀(消息處理)。 (未完待續第二篇)