Android Handle原理(Looper,Handler和Message)三者關(guān)系案例詳解
介紹
前面的內(nèi)容對(duì)Handler做了介紹,也講解了如何使用handler,但是我們并不知道他的實(shí)現(xiàn)原理。本文從源碼的角度來(lái)分析如何實(shí)現(xiàn)的。
首先我們得知道Handler,Looper,Message Queue三者之間的關(guān)系
- Handler封裝了消息的發(fā)送,也負(fù)責(zé)接收消。內(nèi)部會(huì)跟Looper關(guān)聯(lián)。
- Looper 消息封裝的載,內(nèi)部包含了MessageQueue,負(fù)責(zé)從MessageQueue取出消息,然后交給Handler處理
- MessageQueue 就是一個(gè)消息隊(duì)列,負(fù)責(zé)存儲(chǔ)消息,有消息過來(lái)就存儲(chǔ)起來(lái),Looper會(huì)循環(huán)的從MessageQueue讀取消息。
源碼分析
當(dāng)我們new一個(gè)Handler對(duì)象的時(shí)候,看看他的構(gòu)造方法里面做了什么.
public Handler(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 that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
從源碼中我們看到他會(huì)調(diào)用Looper.myLooper方法獲取一個(gè)Looper對(duì)象,然后從Looper對(duì)象獲取到MessageQueue對(duì)象。
Looper myLooper()
跟進(jìn)去看看Looper.myLooper()方法做了什么。這是一個(gè)靜態(tài)方法,可以類名.方法名直接調(diào)用。
public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
這個(gè)方法里面就一行代碼,從sThreadLocal中獲取一個(gè)Looper對(duì)象,sThreadLocal是一個(gè)ThreadLocal對(duì)象,可以在一個(gè)線程中存儲(chǔ)變量。底層是ThreadLocalMap,既然是Map類型那肯定得先set一個(gè)Looper對(duì)象,然后我們才能從sThreadLocal對(duì)象里面get一個(gè)Looper對(duì)象。
ActivityThread main()
說到這里得給大家介紹一個(gè)新的類ActivityThread,ActivityThread類是Android APP進(jìn)程的初始類,它的main函數(shù)是這個(gè)APP進(jìn)程的入口。我們看看這個(gè)main函數(shù)干了什么事情。
public static final void main(String[] args) { ------ Looper.prepareMainLooper(); if (sMainThreadHandler == null) { sMainThreadHandler = new Handler(); } ActivityThread thread = new ActivityThread(); thread.attach(false); if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } Looper.loop(); ----- }
在第二行代碼調(diào)用Looper.prepareMainLooper()方法,第13行調(diào)用了Looper.loop()方法。
Looper prepareMainLooper()
繼續(xù)跟進(jìn)Looper.prepareMainLooper()方法,在這個(gè)方法中第一行代碼調(diào)用了內(nèi)部的prepare方法。prepareMainLooper有點(diǎn)像單例模式中的getInstance方法,只不過getInstance會(huì)當(dāng)時(shí)返回一個(gè)對(duì)象,而prepareMainLooper會(huì)新建一個(gè)Looper對(duì)象,存儲(chǔ)在sThreadLocal中。
public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
Looper prepare()
繼續(xù)跟進(jìn)prepare方法,看第5行代碼,新建了一個(gè)Looper對(duì)象,調(diào)用sThreadLocal.set方法把Looper對(duì)象保存起來(lái)??吹竭@里我想聰明的你們肯定明白了為什么new Handler對(duì)象的時(shí)候調(diào)用Looper.myLooper()方法能從sThreadLocal對(duì)象中取到Looper對(duì)象。
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 構(gòu)造方法
文章開頭我們就講到Looper內(nèi)部包含了MessageQueue,其實(shí)就是在new Looper對(duì)象的時(shí)候就new了一個(gè)MessageQueue對(duì)象。
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
Looper loop()
ActivityThread類main方法中調(diào)用了Looper的兩個(gè)方法,前面我們解釋了prepareMainLooper(),現(xiàn)在來(lái)看第二個(gè)方法loop()。
public static void loop() { final Looper me = myLooper();//獲取Looper對(duì)象 if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue;//從Looper對(duì)象獲取MessageQueue對(duì)象 // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) {//死循環(huán) 一直從MessageQueue中遍歷消息 Message msg = queue.next(); // might block if (msg == null) { return; } // This must be in a local variable, in case a UI event sets the logger final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } final long traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } try { //調(diào)用handler的dispatchMessage方法,把消息交給handler處理 msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); } }
這個(gè)方法的代碼呢比較多。我都給代碼加上了注釋。其實(shí)就是一個(gè)死循環(huán),一直會(huì)從MessageQueue中取消息,如果取到了消息呢,會(huì)執(zhí)行msg.target.dispatchMessage(msg)這行代碼,msg.target就是handler,其實(shí)就是調(diào)用handler的dispatchMessage方法,然后把從MessageQueue中取到的message傳入進(jìn)去。
Handler dispatchMessage()
public void dispatchMessage(Message msg) { //如果callback不為空,說明發(fā)送消息的時(shí)候是post一個(gè)Runnable對(duì)象 if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) {//這個(gè)是用來(lái)攔截消息的 if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg);//最終調(diào)用我們重寫的handleMessage方法 } }
這個(gè)方法對(duì)消息做最后處理,如果是post類型調(diào)用handleCallback方法處理,如果是sendMessage發(fā)送的消息。看我們有沒有攔截消息,如果沒有最終調(diào)用handleMessage方法處理。
Handler handleCallback()
看到這里我們知道為什么post一個(gè)Runnable對(duì)象,run方法執(zhí)行的代碼在主線程了吧,因?yàn)榈讓痈揪蜎]有開啟線程,就只是調(diào)用了run方法而已。
private static void handleCallback(Message message) { message.callback.run(); }
前面我們從創(chuàng)建handler對(duì)象開始,以及創(chuàng)建Looper,創(chuàng)建MessageQueue的整個(gè)流程,現(xiàn)在來(lái)分析下,當(dāng)我們調(diào)用post以及sendMessage方法時(shí),怎么把Message添加到MessageQueue。
Handler post()
調(diào)用了getPostMessage方法,把Runnable傳遞進(jìn)去。
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
Handler getPostMessage()
首先調(diào)用Message.obtain()方法,取出一個(gè)Message對(duì)象,這個(gè)方法之前有講過,然后把Runnable對(duì)象賦值了Message對(duì)象的callback屬性??吹竭@里我們也明白了dispatchMessage方法為什么要先判斷callback是否為空了吧。
private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
Handler enqueueMessage()
在post方法里面調(diào)用了sendMessageDelayed方法,其實(shí)最終調(diào)用的是enqueueMessage方法,所以我這里就直接看enqueueMessage方法源碼。第一行代碼就把handler自己賦值給messgae對(duì)象的target屬性。然后調(diào)用MessageQueue的enqueueMessage方法把當(dāng)前的Messgae添加進(jìn)去。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
總結(jié)
總結(jié):handler負(fù)責(zé)發(fā)送消息,Looper負(fù)責(zé)接收Handler發(fā)送的消息,并直接把消息回傳給Handler自己。MessageQueue就是一個(gè)存儲(chǔ)消息的容器。
到此這篇關(guān)于Android Handle原理(Looper,Handler和Message三者關(guān)系案例詳解的文章就介紹到這了,更多相關(guān)Android Handle原理(Looper,Handler和Message三者關(guān)系內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
簡(jiǎn)單實(shí)現(xiàn)android輪播圖
這篇文章主要為大家詳細(xì)介紹了android輪播圖的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01Android開發(fā)案例手冊(cè)Application跳出dialog
這篇文章主要為大家介紹了Android開發(fā)案例手冊(cè)Application跳出dialog,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06最新Android版本、代號(hào)、對(duì)應(yīng)API/NDK級(jí)別、發(fā)布時(shí)間及市場(chǎng)份額
這篇文章主要介紹了最新Android版本、代號(hào)、對(duì)應(yīng)API/NDK級(jí)別、發(fā)布時(shí)間及市場(chǎng)份額,在開發(fā)Android應(yīng)用時(shí)會(huì)碰到如何選擇Android版本級(jí)API級(jí)別的問題,看完這篇文章后相信這個(gè)問題會(huì)迎刃而解2017-12-12Android貝塞爾曲線實(shí)現(xiàn)加入購(gòu)物車拋物線動(dòng)畫
這篇文章主要為大家詳細(xì)介紹了Android貝塞爾曲線實(shí)現(xiàn)加入購(gòu)物車拋物線動(dòng)畫,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06Android webview注入JS代碼 修改網(wǎng)頁(yè)內(nèi)容操作
這篇文章主要介紹了Android webview注入JS代碼 修改網(wǎng)頁(yè)內(nèi)容操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧2020-03-03Android開發(fā)系列二之窗口Activity的生命周期
這篇文章主要介紹了Android學(xué)習(xí)系列二之窗口Activity的生命周期的相關(guān)資料,需要的朋友可以參考下2016-05-05Android RecyclerView的Item點(diǎn)擊事件實(shí)現(xiàn)整理
這篇文章主要介紹了Android RecyclerView的Item點(diǎn)擊事件實(shí)現(xiàn)整理的相關(guān)資料,需要的朋友可以參考下2017-01-01Android編程實(shí)現(xiàn)動(dòng)態(tài)更新ListView的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)動(dòng)態(tài)更新ListView的方法,結(jié)合實(shí)例形式詳細(xì)分析了ListView的布局及動(dòng)態(tài)更新實(shí)現(xiàn)方法,需要的朋友可以參考下2016-02-02Android鬧鐘啟動(dòng)時(shí)間設(shè)置無(wú)效問題的解決方法
這篇文章主要為大家詳細(xì)介紹了Android鬧鐘啟動(dòng)時(shí)間設(shè)置無(wú)效問題的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06ReactNative (API)AsyncStorage存儲(chǔ)詳解及實(shí)例
這篇文章主要介紹了ReactNative (API)AsyncStorage存儲(chǔ)詳解及實(shí)例的相關(guān)資料,需要的朋友可以參考下2016-10-10