Android線程與消息機(jī)制

OUTLINE

§UI線程

§Looper

§消息機(jī)制

§線程交互

§AsyncTask

§Activity/Service與主線程

UI線程


先從一個(gè)經(jīng)典錯(cuò)誤開(kāi)始:

android.view.ViewRootImpl$CalledFromWrongThreadException:

Only the original thread that created a view hierarchy can touch its views

為什么會(huì)出現(xiàn)這個(gè)錯(cuò)誤?

UI的呈現(xiàn)必須在同一個(gè)線程里面完成。

試想,如果多個(gè)線程可以繪制UI,那么肯定亂套,呈現(xiàn)結(jié)果不可預(yù)期。

因此界面程序必然有一個(gè)UI線程,android,java,windows等都是如此。

Android UI 丈量、排布、繪制最終都是在ViewRootImpl里面完成的。

ViewRootImpl在執(zhí)行UI操作之前,會(huì)進(jìn)行線程檢查。如果當(dāng)前線程不是UI線程,就會(huì)拋出上述異常。

每個(gè)應(yīng)用都對(duì)應(yīng)一個(gè)進(jìn)程,進(jìn)程創(chuàng)建是伴隨一個(gè)主線程創(chuàng)建。這個(gè)主線程就是UI線程。

Android的主線線程的入口在ActivityThread。ActivityThread和普通的java入口類(lèi)一樣,有一個(gè)靜態(tài)main函數(shù),作為主線程的入口。

ActivityThread與Android應(yīng)用生命周期密切相關(guān),后續(xù)會(huì)講到。

Looper


Thread是一個(gè)線性執(zhí)行,界面程序需要持續(xù)存在,因此需要一個(gè)循環(huán),Looper就是Android里面線程循環(huán)的封裝。

這樣說(shuō)還是比較抽象,那么Looper到底是什么?

消息機(jī)制


任務(wù)的循環(huán)執(zhí)行,需要一個(gè)隊(duì)列,可進(jìn)可出,Android使用消息隊(duì)列MessageQueue來(lái)實(shí)現(xiàn)。

一個(gè)Looper綁定一個(gè)Thread,在這個(gè)線程中循環(huán);同時(shí)綁定一個(gè)MessageQueue,在這個(gè)消息隊(duì)列中存取消息;然后,通過(guò)Handler向外接口。通過(guò)Handler把消息加入。

MessageQueue, Looper調(diào)用loop進(jìn)行循環(huán),循環(huán)地從消息隊(duì)列中獲取消息,處理消息。

Message

§消息出隊(duì):MessageQueue::next

§消息入隊(duì)<- 生成消息:綁定Handler

? ?Handler::obtainMessage

?? Message::obtainMessage(Handler)

消息隊(duì)列中的消息是供Looper來(lái)消耗的,Looper通過(guò)MessageQueue的next方法取出消息。這個(gè)過(guò)程是在Looper內(nèi)部完成,我們不需要太過(guò)關(guān)心。

不過(guò)next方法也是比較講究的,這個(gè)方法最終會(huì)調(diào)用native的方法,可能會(huì)等待睡眠,直到IO事件或者消息入隊(duì)把它喚醒。

Android的Message必須綁定到一個(gè)Handler,由這個(gè)Handler來(lái)發(fā)送和處理消息。消息入隊(duì)的時(shí)候會(huì)檢查消息是否綁定了Handler,如果沒(méi)有綁定,會(huì)直接拋出異常。

因此我們通常是Handler::obtainMessage,這個(gè)方法獲得的Message直接綁定到了Handler;另外,也會(huì)Message::obtainMessage,當(dāng)前必須傳遞Handler。

Looper的創(chuàng)建


§Looper.prepare生成Looper實(shí)例

§ThreadLocal映射,將Looper和Thread綁定


Looper.prepare();

?Looper looper = Looper.myLooper();

?...

?Looper.loop();

在線程中調(diào)用Looper.prepare完成Looper的創(chuàng)建。

通過(guò)ThreadLocal映射,Looper與線程綁定。

Looper創(chuàng)建時(shí)生成了MessageQueue。

Thread和ThreadLocal都是java的東西,Looper是Android的。Thread和Looper之前的綁定使用了ThreadLocal。ThreadLocal就是線程的存儲(chǔ)器,它是一個(gè)通用設(shè)計(jì),它為每個(gè)線程存儲(chǔ)數(shù)據(jù),從每個(gè)線程進(jìn)來(lái)看到對(duì)應(yīng)的線程的數(shù)據(jù)。因此Android的Looper很好地利用了這一點(diǎn),使用ThreadLocal,從每個(gè)線程進(jìn)來(lái)看到的Looper都是綁定的Looper。

MainLooper的創(chuàng)建

§ActivityThread main入口

?Looper. prepareMainLooper();

?...

?Looper.loop();

主線程入口處(ActivityThread的main入口),調(diào)用Looper.prepareMainLooper,完成MainLooper的創(chuàng)建。再調(diào)用loop讓主線程循環(huán)起來(lái)。

MainLooper作為靜態(tài)變量保存在Looper中,可通過(guò)getMainLooper獲取。

?Handler的創(chuàng)建

§Handler——Looper的對(duì)外接口

§Handler創(chuàng)建 <- Looper實(shí)例

?主線程Handler:直接獲取MainLooper來(lái)創(chuàng)建。

?其他線程Handler:必須在調(diào)用Looper.prepare之后創(chuàng)建。

eg:

HandlerThread::onLooperPrepared

?不指定Looper,使用當(dāng)前線程Looper。

Handler創(chuàng)建需要指定Looper,因此Handler的創(chuàng)建需要在調(diào)用Looper.prepare之后(HandlerThread.onLooperPrepared),否則會(huì)報(bào)異常。

如果沒(méi)有Looper,默認(rèn)使用當(dāng)前線程綁定的Looper。

因此,通常在主線程可以任意創(chuàng)建Handler,因?yàn)镸ainLooper在主線程啟動(dòng)時(shí)已經(jīng)prepare。

而在其他線程創(chuàng)建Handler時(shí)需要先調(diào)用Looper.prepare。

非主線線程Handler典型創(chuàng)建方法是通過(guò)HandlerThread。HandlerThread是Android聯(lián)結(jié)Handler和線程的封裝,它用onLooperPrepared回調(diào)提供給外界創(chuàng)建Handler。

?線程的創(chuàng)建

§線性執(zhí)行:直接或間接new Thread

§循環(huán):

?????????????? 1. 基于Looper的Thread ->HandlerThread

?????????????? 2. 自己為線程實(shí)現(xiàn)循環(huán)機(jī)制

線程交互


線程之間的交互通過(guò)消息機(jī)制完成。具體來(lái)說(shuō),A線程需要發(fā)送消息到B線程,需要通過(guò)持有B線程Looper的Handler發(fā)送消息。

eg:? A線程需要操作B線程

A發(fā)送消息給B

?A發(fā)送:A需要Handler實(shí)例

?給B:Handler實(shí)例必須持有B線程的Looper。

實(shí)現(xiàn):

?B線程生成Handler

?Handler定義消息和消息處理

主線程與非主線程交互

§主線程執(zhí)行非主線程操作

?獲取非主線程Handler發(fā)送消息。?? ? 針對(duì)持久存在的非主線程處理

?通過(guò)AsyncTask::doInBackground執(zhí)行。 針對(duì)臨時(shí)存在的后臺(tái)線程處理

-View.post(Runnable)。 ? ? 在View中執(zhí)行。

-Activity.runOnUiThread。 ? ? 在Activity中執(zhí)行。

§非主線程執(zhí)行主線程操作

?獲取MainLooper生成主線程Handler。 ?針對(duì)持久的主線程處理

?eg:ViewRootHander,Activity的Handler

?通過(guò)AsyncTask::onProgressUpdate或onPostExecute。 ? 針對(duì)臨時(shí)主線程處理

?通過(guò)臨時(shí)new Handler(傳遞MainLooper)來(lái)post執(zhí)行。

View的post函數(shù)將Runnable加入到ViewRootImpl的執(zhí)行隊(duì)列中,在下次ViewRootImpl執(zhí)行tranversal時(shí),將隊(duì)列任務(wù)加入一個(gè)主線程的Handler的消息隊(duì)列。

Activity的runOnUiThread后文再詳細(xì)講解。

AsyncTask是Android對(duì)主線程和后臺(tái)線程處理的一個(gè)封裝,它是線性執(zhí)行的,不作循環(huán)。

AsyncTask


§主線程執(zhí)行——持有MainLooper的Handler靜態(tài)成員sHandler,在主線程入口處初始化。

§后臺(tái)執(zhí)行——新建線程,設(shè)置為優(yōu)先級(jí):

THREAD_PRIORITY_BACKGROUND

onProgressUpdate

onPostExecute? 主線程

doInBackground???? 后臺(tái)線程

onPreExecute????? 取決于調(diào)用線程

AsyncTask持有一個(gè)靜態(tài)Handler,由主線程入口處初始化創(chuàng)建,因此它持有MainLooper。onProgressUpdate和onPostExecute是通過(guò)該Handler執(zhí)行的,因此都是主線程操作。

類(lèi)的靜態(tài)成員和靜態(tài)塊,是在類(lèi)加載的時(shí)候執(zhí)行的。在主線程入口的地方,沒(méi)有必須創(chuàng)建一個(gè)AsyncTask實(shí)例,但是需要為它初始Handler,因此調(diào)用AsyncTask一個(gè)無(wú)用的靜態(tài)方法init,僅僅是為了初始化Handler。

AsyncTask的后臺(tái)執(zhí)行是通過(guò)創(chuàng)建一個(gè)新的線程,非設(shè)置線程優(yōu)先級(jí)為Background,因此它執(zhí)行后臺(tái)操作。

onProgressUpdate和onPostExecute是通過(guò)該Handler執(zhí)行的,因此都是主線程操作。doInBackground是新建線程(THREAD_PRIORITY_BACKGROUND優(yōu)先級(jí)),因此是后臺(tái)線程操作。onPreExecute在execute中執(zhí)行,所在線程取決于調(diào)用的線程。

線程優(yōu)先級(jí)

§設(shè)置線程優(yōu)先級(jí)

?android.os.Process.setThreadPriority:[-20, 19]。越小優(yōu)先級(jí)越高

?java.lang.Thread.setPriority:[1, 10]。越大優(yōu)先級(jí)越高。

§THREAD_PRIORITY_BACKGROUND(10):標(biāo)準(zhǔn)的后臺(tái)優(yōu)先級(jí)

§默認(rèn)線程優(yōu)先級(jí):THREAD_PRIORITY_DEFAULT(0),中等

設(shè)置線程優(yōu)先級(jí)有兩種方式:

android.os.Process.setThreadPriority:[-20,19]。越小優(yōu)先級(jí)越高

java.lang.Thread.setPriority:[1, 10]。越大優(yōu)先級(jí)越高。

優(yōu)先級(jí)調(diào)度是底層實(shí)現(xiàn)的,沒(méi)有具體深入。優(yōu)先級(jí)的設(shè)置和執(zhí)行結(jié)果是沒(méi)有辦法準(zhǔn)確預(yù)期的,但是可以肯定的是Process.setThreadPriority的效果更符合預(yù)期。

Activity/Service與主線程


§Activity和Service運(yùn)行在主線程(ActivityThread$H)

?onCreate,onResume,…

§Activity Handler

?與AsyncTask類(lèi)似,Activity內(nèi)部有一個(gè)持有MainLooper的Handler。

Acitivty的啟動(dòng)過(guò)程是通過(guò)ActivityThread.H這樣一個(gè)Handler來(lái)調(diào)用的,這是一個(gè)主線程的Handler,因此主線程的啟動(dòng)都在主線程。

另外,與AsyncTask類(lèi)似,Activity內(nèi)部有一個(gè)持有MainLooper的Handler。因此Activity提供了一個(gè)runOnUiThread的方法,方便直接執(zhí)行UI操作。


【茶工坊】

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容