Android 消息處理機制(Looper、Handler、MessageQueue,Message)

Android消息處理機制估計都被寫爛了,但是依然還是要寫一下,因為Android應用程序是通過消息來驅動的,Android某種意義上也可以說成是一個以消息驅

動的系統(tǒng),UI、事件、生命周期都和消息處理機制息息相關,并且消息處理機制在整個Android知識體系中也是尤其重要,在太多的源碼分析的文章講得比

較繁瑣,很多人對整個消息處理機制依然是懵懵懂懂,這篇文章通過一些問答的模式結合Android主線程(UI線程)的工作原理來講解,源碼注釋很全,還

有結合流程圖,如果你對Android 消息處理機制還不是很理解,我相信只要你靜下心來耐心的看,肯定會有不少的收獲的。

概述

1、我們先說下什么是Android消息處理機制?

消息處理機制本質:一個線程開啟循環(huán)模式持續(xù)監(jiān)聽并依次處理其他線程給它發(fā)的消息。

簡單的說:一個線程開啟一個無限循環(huán)模式,不斷遍歷自己的消息列表,如果有消息就挨個拿出來做處理,如果列表沒消息,自己就堵塞(相當于wait,

讓出cpu資源給其他線程),其他線程如果想讓該線程做什么事,就往該線程的消息隊列插入消息,該線程會不斷從隊列里拿出消息做處理。

2、Android消息處理機制的工作原理?

打個比方:公司類比App

PM 的主要工作是設計產品,寫需求文檔,改需求,中途改需求,提測前改需求...

UI? 主要工作是UI設計,交互等。

RD 工作我就不說了

CEO 不解釋。

公司開創(chuàng)之后(App啟動),那么CEO開始干活了(主線程【UI線程】啟動),這時候CEO開啟了無限循環(huán)工作狂模式,自己的公司沒辦法?。ㄏ喈斢赨I主線程轉成Looper線程【源碼里面有】)CEO招了一名RD(new Handler 實例)并把告訴PM和UI,如果你們有什么任務和需求就讓RD(Handler實例)轉告給我(CEO)。RD會把PM和UI的需求(Message)一條條記到CEO的備忘錄里(MessageQueue)。CEO 無限循環(huán)的工作就是不斷查看備忘錄,看有什么任務要做,有任務就從備忘錄一條一條拿出任務來,然后交給這一名RD(Handler 實例)去處理(畢竟CEO 不會寫代碼 囧...)。當然如果備忘錄都做完了,這時候CEO就會去睡覺(線程堵塞【簡單理解成線程wait】,讓出CPU資源,讓其他線程去執(zhí)行)。但是這個備忘錄有個特殊的功能就是沒有任務的時候突然插入第一條任務(從無到有)就會有鬧鐘功能叫醒CEO起床繼續(xù)處理備忘錄。 整個消息處理機制的工作原理基本就是這樣的。后面會有源碼分析,你再來結合這個場景,會更好理解一些。

這里先給一張Android消息處理機制流程圖和具體執(zhí)行動畫,如果看不懂沒事,接著往下看(后面會結合Android UI主線程來講解),然后結合著圖和動畫一塊看更能理解整個機制的實現原理。

3、Looper、Handler、MessageQueue,Message作用和存在的意義?

Looper

我們知道一個線程是一段可執(zhí)行的代碼,當可執(zhí)行代碼執(zhí)行完成后,線程生命周期便會終止,線程就會退出,那么做為App的主線程,如果代碼段執(zhí)行完了會怎樣?,那么就會出現App啟動后執(zhí)行一段代碼后就自動退出了,這是很不合理的。所以為了防止代碼段被執(zhí)行完,只能在代碼中插入一個死循環(huán),那么代碼就不會被執(zhí)行完,然后自動退出,怎么在在代碼中插入一個死循環(huán)呢?那么Looper出現了,在主線程中調用Looper.prepare()...Looper.loop()就會變當前線程變成Looper線程(可以先簡單理解:無限循環(huán)不退出的線程),Looper.loop()方法里面有一段死循環(huán)的代碼,所以主線程會進入while(true){...}的代碼段跳不出來,但是主線程也不能什么都不做吧?其實所有做的事情都在while(true){...}里面做了,主線程會在死循環(huán)中不斷等其他線程給它發(fā)消息(消息包括:Activity啟動,生命周期,更新UI,控件事件等),一有消息就根據消息做相應的處理,Looper的另外一部分工作就是在循環(huán)代碼中會不斷從消息隊列挨個拿出消息給主線程處理。

MessageQueue

MessageQueue 存在的原因很簡單,就是同一線程在同一時間只能處理一個消息,同一線程代碼執(zhí)行是不具有并發(fā)性,所以需要隊列來保存消息和安排每個消息的處理順序。多個其他線程往UI線程發(fā)送消息,UI線程必須把這些消息保持到一個列表(它同一時間不能處理那么多任務),然后挨個拿出來處理,這種設計很簡單,我們平時寫代碼其實也經常這么做。每一個Looper線程都會維護這樣一個隊列,而且僅此一個,這個隊列的消息只能由該線程處理。

Handler

簡單說Handler用于同一個進程的線程間通信。Looper讓主線程無限循環(huán)地從自己的MessageQueue拿出消息處理,既然這樣我們就知道處理消息肯定是在主線程中處理的,那么怎樣在其他的線程往主線程的隊列里放入消息呢?其實很簡單,我們知道在同一進程中線程和線程之間資源是共享的,也就是對于任何變量在任何線程都是可以訪問和修改的,只要考慮并發(fā)性做好同步就行了,那么只要拿到MessageQueue 的實例,就可以往主線程的MessageQueue放入消息,主線程在輪詢的時候就會在主線程處理這個消息。那么怎么拿到主線程 MessageQueue的實例,是可以拿到的(在主線程下mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),但是Google 為了統(tǒng)一添加消息和消息的回調處理,又專門構建了Handler類,你只要在主線程構建Handler類,那么這個Handler實例就獲取主線程MessageQueue實例的引用(獲取方式mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),Handler 在sendMessage的時候就通過這個引用往消息隊列里插入新消息。Handler 的另外一個作用,就是能統(tǒng)一處理消息的回調。這樣一個Handler發(fā)出消息又確保消息處理也是自己來做,這樣的設計非常的贊。具體做法就是在隊列里面的Message持有Handler的引用(哪個handler 把它放到隊列里,message就持有了這個handler的引用),然后等到主線程輪詢到這個message的時候,就來回調我們經常重寫的Handler的handleMessage(Message msg)方法。

Message

Message 很簡單了,你想讓主線程做什么事,總要告訴它吧,總要傳遞點數據給它吧,Message就是這個載體。

源碼分析

接下來我們會結合App主線程(UI線程)來講解,從App啟動后一步一步往下走分析整個Android的消息處理機制,首先在

ActivityThread類有我們熟悉的main的函數,App啟動的代碼的入口就在這里,UI線程本來只是一個普通線程,在這里會把UI線程轉換成

Looper線程,什么是Looper線程,不急往下看就知道了。

publicfinalclassActivityThread{publicstaticfinalvoidmain(String[] args){? ? ??

......? ? ? ? Looper.prepareMainLooper();? ? ?

?......? ? ? ? ActivityThread thread =newActivityThread();? ? ??

thread.attach(false);

if(sMainThreadHandler ==null) {? ??

? ? ? ? ? sMainThreadHandler = thread.getHandler();? ??

? }? ? ??

......? ? ? ? Looper.loop();? ? ? ? ......??

}}

首先執(zhí)行的是Looper.prepareMainLooper()我們來看下Looper里面的這個方法做了什么?

注:看之前先稍微了解下ThreadLocal是什么?

ThreadLocal: 線程本地存儲區(qū)(Thread Local Storage,簡稱為TLS),每個線程都有自己的私有的本地存儲區(qū)域,不同線程之間彼此不能訪問對方的TLS區(qū)域。這里線程自己的本地存儲區(qū)域存放是線程自己的Looper。具體看下ThreadLocal.java 的源碼!

publicfinalclassLooper{

// sThreadLocal 是static的變量,可以先簡單理解它相當于map,key是線程,value是Looper,

//那么你只要用當前的線程就能通過sThreadLocal獲取當前線程所屬的Looper。staticfinalThreadLocal sThreadLocal =newThreadLocal();//主線程(UI線程)的Looper 單獨處理,是static類型的,通過下面的方法getMainLooper()//可以方便的獲取主線程的Looper。privatestaticLooper sMainLooper;//Looper 所屬的線程的消息隊列finalMessageQueue mQueue;//Looper 所屬的線程finalThread mThread;publicstaticvoidprepare(){? ? ??

prepare(true);? ? }

privatestaticvoidprepare(booleanquitAllowed){//如果線程的TLS已有數據,則會拋出異常,一個線程只能有一個Looper,prepare不能重復調用。

if(sThreadLocal.get() !=null) {

thrownewRuntimeException("Only one Looper may be created per thread");? ? ? ? }

//往線程的TLS插入數據,簡單理解相當于map.put(Thread.currentThread(),new Looper(quitAllowed));sThreadLocal.set(newLooper(quitAllowed));? ? }//實際上是調用? prepare(false),并然后給sMainLooper賦值。

publicstaticvoidprepareMainLooper(){? ? ?

? prepare(false);synchronized(Looper.class) {

if(sMainLooper !=null) {

thrownewIllegalStateException("The main Looper has already been prepared.");? ? ? ?

? ? }? ? ? ? ? ? sMainLooper = myLooper();? ? ?

? }? ? }//static 方法,方便獲取主線程的

Looper.publicstaticLoopergetMainLooper(){

synchronized(Looper.class) {returnsMainLooper;? ? ?

? }? ? }

publicstatic@NullableLoopermyLooper(){//具體看ThreadLocal類的源碼的get方法,//簡單理解相當于map.get(Thread.currentThread()) 獲取當前線程的LooperreturnsThreadLocal.get();? ? }}

看了上面的代碼(仔細看下注釋),我們發(fā)現Looper.prepareMainLooper()做的事件就是new了一個Looper實例并放入Looper類下面一個static的ThreadLocal sThreadLocal靜態(tài)變量中,同時給sMainLooper賦值,給sMainLooper賦值是為了方便通過Looper.getMainLooper()快速獲取主線程的Looper,sMainLooper是主線程的Looper可能獲取會比較頻繁,避免每次都到 sThreadLocal 去查找獲取。

接下來重點是看下Looper的構造函數,看看在new Looper的時候做了什么事?

privateLooper(booleanquitAllowed){? ? ??

mQueue =newMessageQueue(quitAllowed);? ? ? ?

mThread = Thread.currentThread();}

看到沒有,這時候給當前線程創(chuàng)建了消息隊列MessageQueue,并且讓Looper持有MessageQueue的引用。執(zhí)行完Looper.prepareMainLooper()之后,主線程從普通線程轉成一個Looper線程。目前的主線程線程已經有一個Looper對象和一個消息隊列mQueue,引用關系如下圖:(主線程可以輕松獲取它的Looper,主線程的Looper持有主線程消息隊列的引用)

具體如何獲取主線程的Looper對象和消息列表呢?

//在主線程中執(zhí)行mLooper = Looper.myLooper();mQueue = mLooper.mQueue//或者mLooper=Looper.getMainLooper()

接下來回到ActivityThread 的main函數,執(zhí)行完Looper.prepareMainLooper()之后下一句代碼是ActivityThread thread = new ActivityThread();這句話就是創(chuàng)建一下ActivityThread對象,這邊需要注意的時候ActivityThread并不是一個線程,它并沒有繼承Thread,而只是一個普通的類public final class ActivityThread{...}ActivityThread的構造函數并沒有做什么事只是初始化了資源管理器。

ActivityThread() {

mResourcesManager = ResourcesManager.getInstance();

}

接著往下看下一行代碼

ActivityThread thread =newActivityThread();//建立Binder通道 (創(chuàng)建新線程)thread.attach(false);

thread.attach(false);便會創(chuàng)建一個Binder線程(具體是指ApplicationThread,該Binder線程會通過想Handler將Message發(fā)送給主線程,之后講)。我們之前提到主線程最后會進入無限循環(huán)當中,如果沒有在進入死循環(huán)之前創(chuàng)建其他線程,那么待會誰會給主線程發(fā)消息呢?,沒錯就是在這里創(chuàng)建了這個線程,這個線程會接收來自系統(tǒng)服務發(fā)送來的一些事件封裝了Message并發(fā)送給主線程,主線程在無限循環(huán)中從隊列里拿到這些消息并處理這些消息。(Binder線程發(fā)生的消息包括LAUNCH_ACTIVITY,PAUSE_ACTIVITY等等)

繼續(xù)回到mian 函數的下一句代碼Looper.loop()那么重點來了,我們來看下Looper.loop()的源碼:

publicstaticvoidloop(){finalLooper me = myLooper();//獲取TLS存儲的Looper對象,獲取當前線程的Looperif(me ==null) {thrownewRuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");? ? }finalMessageQueue queue = me.mQueue;//獲取Looper對象中的消息隊列....for(;;) {//主線程開啟無限循環(huán)模式Message msg = queue.next();//獲取隊列中的下一條消息,可能會線程阻塞if(msg ==null) {//沒有消息,則退出循環(huán),退出消息循環(huán),那么你的程序也就可以退出了return;? ? ? ? }? ? ? ? ....//分發(fā)Message,msg.target 是一個Handler對象,哪個Handler把這個Message發(fā)到隊列里,//這個Message會持有這個Handler的引用,并放到自己的target變量中,這樣就可以回調我們重寫//的handler的handleMessage方法。msg.target.dispatchMessage(msg);? ? ? ? ....? ? ? ? ....? ? ? ? msg.recycleUnchecked();//將Message回收到消息池,下次要用的時候不需要重新創(chuàng)建,obtain()就可以了。}}

上面的代碼,大家具體看下注釋,這時候主線程(UI線程)執(zhí)行到這一步就進入了死循環(huán),不斷地去拿消息隊列里面的消息出來處理?那么問題來了

1、UI線程一直在這個循環(huán)里跳不出來,主線程不會因為Looper.loop()里的死循環(huán)卡死嗎,那還怎么執(zhí)行其他的操作呢?

在looper啟動后,主線程上執(zhí)行的任何代碼都是被looper從消息隊列里取出來執(zhí)行的。也就是說主線程之后都是通過其他線程給它發(fā)消息來實

現執(zhí)行其他操作的。生命周期的回調也是如此的,系統(tǒng)服務ActivityManagerService通過Binder發(fā)送IPC調用給APP進

程,App進程接到到調用后,通過App進程的Binder線程給主線程的消息隊列插入一條消息來實現的。

2、主線程是UI線程和用戶交互的線程,優(yōu)先級應該很高,主線程的死循環(huán)一直運行是不是會特別消耗CPU資源嗎?App進程的其他線程怎么辦?

這基本是一個類似生產者消費者的模型,簡單說如果在主線程的MessageQueue沒有消息時,就會阻塞在loop的queue.next()方法里,這時候主線程會釋放CPU資源進入休眠狀態(tài),直到有下個消息進來時候就會喚醒主線程,在2.2 版本以前,這套機制是用我們熟悉的線程的wait和notify 來實現的,之后的版本涉及到Linux pipe/epoll機制,通過往pipe管道寫端寫入數據來喚醒主線程工作。原理類似于I/O,讀寫是堵塞的,不占用CPU資源。

所以上面代碼的重點是queue.next()的函數,其他的我們就不多說了,我們來看下queue.next()的源碼(主要還是看注釋):

Messagenext()finallongptr= mPtr;if(ptr ==0) {returnnull;? ? ? ? }intpendingIdleHandlerCount = -1;// -1 only during first iteration//nextPollTimeoutMillis 表示nativePollOnce方法需要等待nextPollTimeoutMillis//才會返回intnextPollTimeoutMillis =0;for(;;) {if(nextPollTimeoutMillis !=0) {? ? ? ? ? ? ? ? Binder.flushPendingCommands();? ? ? ? ? ? }//讀取消息,隊里里沒有消息有可能會堵塞,兩種情況該方法才會返回(代碼才能往下執(zhí)行)//一種是等到有消息產生就會返回,//另一種是當等了nextPollTimeoutMillis時長后,nativePollOnce也會返回nativePollOnce(ptr, nextPollTimeoutMillis);//nativePollOnce 返回之后才能往下執(zhí)行synchronized(this) {// Try to retrieve the next message.? Return if found.finallongnow = SystemClock.uptimeMillis();? ? ? ? ? ? ? ? Message prevMsg =null;? ? ? ? ? ? ? ? Message msg = mMessages;if(msg !=null&& msg.target ==null) {// 循環(huán)找到一條不是異步而且msg.target不為空的messagedo {? ? ? ? ? ? ? ? ? ? ? ? prevMsg = msg;? ? ? ? ? ? ? ? ? ? ? ? msg = msg.next;? ? ? ? ? ? ? ? ? ? }while(msg !=null&& !msg.isAsynchronous());? ? ? ? ? ? ? ? }if(msg !=null) {if(now < msg.when) {// 雖然有消息,但是還沒有到運行的時候,像我們經常用的postDelay,//計算出離執(zhí)行時間還有多久賦值給nextPollTimeoutMillis,//表示nativePollOnce方法要等待nextPollTimeoutMillis時長后返回nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);? ? ? ? ? ? ? ? ? ? }else{// 獲取到消息mBlocked =false;//鏈表一些操作,獲取msg并且刪除該節(jié)點if(prevMsg !=null)? ? ? ? ? ? ? ? ? ? ? ? ? ? prevMsg.next = msg.next;? ? ? ? ? ? ? ? ? ? ? ? }else{? ? ? ? ? ? ? ? ? ? ? ? ? ? mMessages = msg.next;? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? ? ? msg.next =null;? ? ? ? ? ? ? ? ? ? ? ? msg.markInUse();//返回拿到的消息returnmsg;? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? }else{//沒有消息,nextPollTimeoutMillis復位nextPollTimeoutMillis = -1;? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? .....? ? ? ? ? ? ? ? .....? ? }

nativePollOnce()很重要,是一個native的函數,在native做了大量的工作,主要涉及到epoll機制的處理(在沒有消息處理時阻塞在管道的讀端),具體關于native相關的源碼本篇文章不涉及,感興趣的同學可以網上找找,有不少分析得比較深。

分析到這里,從應用啟動創(chuàng)建Looper,創(chuàng)建消息隊列,到進入loop方法執(zhí)行無限循環(huán)中,那么這一塊就告一段落了,主線程已經在死循環(huán)里輪詢等待消息了,接下來我們就要再看看,系統(tǒng)是怎么發(fā)消息給主線程的,主線程是怎么處理這些個消息的?

在準備啟動一個Activity的時候,系統(tǒng)服務進程下的ActivityManagerService(簡稱AMS)線程會通過Binder發(fā)送IPC調用給APP進程,App進程接到到調用后,通過App進程下的Binder線程最終調用ActivityThread類下面的scheduleLaunchActivity方法來準備啟動Activity,看下scheduleLaunchActivity方法:

注:Binder線程:具體是指ApplicationThread,在App進程中接受系統(tǒng)進程傳遞過來的信息的線程(在主線程進入死循環(huán)之前創(chuàng)建了這個線程)。

//這個方法不是在主線程調用,是Binder線程下調用的publicfinalvoidscheduleLaunchActivity(Intent intent, IBinder token,intident,? ? ? ? ? ? ? ? ActivityInfo info, Configuration curConfig, Configuration overrideConfig,? ? ? ? ? ? ? ? CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,intprocState, Bundle state, PersistableBundle persistentState,? ? ? ? ? ? ? ? List pendingResults, List pendingNewIntents,booleannotResumed,booleanisForward, ProfilerInfo profilerInfo){? ? ? ? ? ? updateProcessState(procState,false);? ? ? ? ? ? ActivityClientRecord r =newActivityClientRecord();? ? ? ? ? ? r.token = token;? ? ? ? ? ? r.ident = ident;? ? ? ? ? ? r.intent = intent;? ? ? ? ? ? r.referrer = referrer;? ? ? ? ? ? r.voiceInteractor = voiceInteractor;? ? ? ? ? ? r.activityInfo = info;? ? ? ? ? ? r.compatInfo = compatInfo;? ? ? ? ? ? r.state = state;? ? ? ? ? ? r.persistentState = persistentState;? ? ? ? ? ? r.pendingResults = pendingResults;? ? ? ? ? ? r.pendingIntents = pendingNewIntents;? ? ? ? ? ? r.startsNotResumed = notResumed;? ? ? ? ? ? r.isForward = isForward;? ? ? ? ? ? r.profilerInfo = profilerInfo;? ? ? ? ? ? r.overrideConfig = overrideConfig;? ? ? ? ? ? updatePendingConfiguration(curConfig);? ? ? ? ? ? sendMessage(H.LAUNCH_ACTIVITY, r);? }

把啟動一些信息封裝成ActivityClientRecord之后,最后一句調用sendMessage(H.LAUNCH_ACTIVITY, r);我們接著往下看:

privatevoidsendMessage(intwhat, Object obj){? ? ? ? sendMessage(what, obj,0,0,false);? ? }privatevoidsendMessage(intwhat, Object obj,intarg1,intarg2,booleanasync){? ? ? ? Message msg = Message.obtain();? ? ? ? msg.what = what;? ? ? ? msg.obj = obj;? ? ? ? msg.arg1 = arg1;? ? ? ? msg.arg2 = arg2;if(async) {? ? ? ? ? ? msg.setAsynchronous(true);? ? ? ? }? ? ? ? mH.sendMessage(msg);? ? }

看到沒有,最后啟動Activity的信息都封裝一個Message,但是這里有個問題了,之前在分析main函數的時候,完全沒給出往主線程消息隊列插入消息的方式,這里有了消息,但是怎么發(fā)到主線程的消息隊列呢?最后一句又是重點mH.sendMessage(msg);mH 是什么呢?難道是Handler,我們來看下它是什么東西?

我們看了下ActivityThread 的成員變量,發(fā)現一句初始化的代碼

finalHmH =newH();

繼續(xù)往下看H是什么?

publicfinalclassActivityThread{? ? ....finalH mH =newH();? ? ....privateclassHextendsHandler{? ? ....? ? ....publicvoidhandleMessage(Message msg){if(DEBUG_MESSAGES) Slog.v(TAG,">>> handling: "+ codeToString(msg.what));switch(msg.what) {caseLAUNCH_ACTIVITY: {? ? ? ? ? ? ? ? ? ? Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,"activityStart");finalActivityClientRecord r = (ActivityClientRecord) msg.obj;? ? ? ? ? ? ? ? ? ? r.packageInfo = getPackageInfoNoCheck(? ? ? ? ? ? ? ? ? ? ? ? ? ? r.activityInfo.applicationInfo, r.compatInfo);? ? ? ? ? ? ? ? ? ? handleLaunchActivity(r,null);? ? ? ? ? ? ? ? ? ? Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);? ? ? ? ? ? ? ? }break;caseRELAUNCH_ACTIVITY: {? ? ? ? ? ? ? ? ? ? Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,"activityRestart");? ? ? ? ? ? ? ? ? ? ActivityClientRecord r = (ActivityClientRecord)msg.obj;? ? ? ? ? ? ? ? ? ? handleRelaunchActivity(r);? ? ? ? ? ? ? ? ? ? Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);? ? ? ? ? ? ? ? }break;casePAUSE_ACTIVITY:? ? ? ? ? ? ? ? ? ? Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,"activityPause");? ? ? ? ? ? ? ? ? ? handlePauseActivity((IBinder)msg.obj,false, (msg.arg1&1) !=0, msg.arg2,? ? ? ? ? ? ? ? ? ? ? ? ? ? (msg.arg1&2) !=0);? ? ? ? ? ? ? ? ? ? maybeSnapshot();? ? ? ? ? ? ? ? ? ? Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;? ? ? ? ? ? ? ? ? .....? ? ? ? }? ? ? ? .....? ? ? ? .....? ? }}

H 果不出其然是Handler,而且是ActivityThread的內部類,看了一下它的handleMessage方法,LAUNCH_ACTIVITY、PAUSE_ACTIVITY、RESUME_ACTIVITY...好多好多,H 類幫我們處理了好多聲明周期的事情。那么再回到mH.sendMessage(msg)這句代碼上,在Binder線程執(zhí)行mH.sendMessage(msg);,由主線程創(chuàng)建的Handler mH實例發(fā)送消息到主線程的消息隊列里,消息隊列從無到有,主線程堵塞被喚醒,主線程loop拿到消息,并回調mH的handleMessage方法處理LAUNCH_ACTIVITY 等消息。從而實現Activity的啟動。

講到這里,基本一個啟動流程分析完了,大家可能比較想知道的是mH.sendMessage(msg);關于Hanlder是怎么把消息發(fā)到主線程的消息隊列的?我們接下來就講解下Handler,首先看下Handler的源碼!我們先來看看我們經常用的Handler的無參構造函數,實際調用的是Handler(Callback callback, boolean async)構造函數(看注釋)

publicHandler(){this(null,false); }publicHandler(Callback callback,booleanasync){//不是static 發(fā)出可能內存泄露的警告!if(FIND_POTENTIAL_LEAKS) {finalClass 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());? ? ? ? ? ? }? ? ? ? }//獲取當前線程的Looper,還記得前面講過 Looper.myLooper()方法了嗎?//Looper.myLooper()內部實現可以先簡單理解成:map.get(Thread.currentThread())//獲取當前線程的LoopermLooper = Looper.myLooper();if(mLooper ==null) {//當前線程不是Looper 線程,沒有調用Looper.prepare()給線程創(chuàng)建Looper對象thrownewRuntimeException("Can't create handler inside thread that has not called Looper.prepare()");? ? ? ? }//讓Handler 持有當前線程消息隊列的引用mQueue = mLooper.mQueue;//這些callback先不管,主要用于handler的消息發(fā)送的回調,優(yōu)先級是比handlerMessage高,但是不常用mCallback = callback;? ? ? ? mAsynchronous = async;? ? }

上面的代碼說明了下面幾個問題:

1、Handler 對象在哪個線程下構建(Handler的構造函數在哪個線程下調用),那么Handler 就會持有這個線程的Looper引用和這個線程的消息隊列的引用。因為持有這個線程的消息隊列的引用,意味著這個Handler對象可以在任意其他線程給該線程的消息隊列添加消息,也意味著Handler的handlerMessage 肯定也是在該線程執(zhí)行的。

2、如果該線程不是Looper線程,在這個線程new Handler 就會報錯!

3、上面兩點綜合說明了下面一段很常見的代碼:把普通線程轉成Looper線程的代碼,為什么在Looper.prepare()和Looper.loop()中間要創(chuàng)建Handler:

classLooperThreadextendsThread{//其他線程可以通過mHandler這個引用給該線程的消息隊列添加消息publicHandler mHandler;publicvoidrun(){? ? ? ? ? ? Looper.prepare();//需要在線程進入死循環(huán)之前,創(chuàng)建一個Handler實例供外界線程給自己發(fā)消息mHandler =newHandler() {publicvoidhandleMessage(Message msg){//Handler 對象在這個線程構建,那么handleMessage的方法就在這個線程執(zhí)行}? ? ? ? ? ? };? ? ? ? ? ? Looper.loop();? ? ? ? }? ? }

那么接下來,我們接著往下看Handler的sendMessage(msg)方法,這個方法也是比較重要的,也比較常用,Handler 有很多sendXXXX開頭的方法sendMessageAtTime、sendEmptyMessageDelayed、sendEmptyMessage等等,都是用來給消息隊列添加消息的,那么這些方法最終都會調用enqueueMessage來實現消息進入隊列:

privatebooleanenqueueMessage(MessageQueue queue, Message msg,longuptimeMillis){//這句話很重要,讓消息持有當前Handler的引用,在消息被Looper線程輪詢到的時候//回調handler的handleMessage方法msg.target =this;if(mAsynchronous) {? ? ? ? ? ? msg.setAsynchronous(true);? ? ? ? }//調用MessageQueue 的enqueueMessage 方法把消息放入隊列returnqueue.MessageQueue(msg, uptimeMillis);? ? }

我們再來看下MessageQueue 的MessageQueue(msg, uptimeMillis)方法:

booleanenqueueMessage(Message msg,longwhen){// msg 必須有target也就是必須有handlerif(msg.target ==null) {thrownewIllegalArgumentException("Message must have a target.");? ? ? ? }if(msg.isInUse()) {thrownewIllegalStateException(msg +" This message is already in use.");? ? ? ? }//插入消息隊列的時候需要做同步,因為會有多個線程同時做往這個隊列插入消息synchronized(this) {if(mQuitting) {? ? ? ? ? ? ? ? IllegalStateException e =newIllegalStateException(? ? ? ? ? ? ? ? ? ? ? ? msg.target +" sending message to a Handler on a dead thread");? ? ? ? ? ? ? ? Log.w(TAG, e.getMessage(), e);? ? ? ? ? ? ? ? msg.recycle();returnfalse;? ? ? ? ? ? }? ? ? ? ? ? msg.markInUse();//when 表示這個消息執(zhí)行的時間,隊列是按照消息執(zhí)行時間排序的//如果handler 調用的是postDelay 那么when=SystemClock.uptimeMillis()+delayMillismsg.when = when;? ? ? ? ? ? Message p = mMessages;booleanneedWake;if(p ==null|| when ==0|| when < p.when) {// p==null 表示當前消息隊列沒有消息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;//將消息放到隊列的確切位置,隊列是按照msg的when 排序的,鏈表操作自己看咯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.nextprev.next = msg;? ? ? ? ? ? }// 如果需要喚醒Looper線程,這里調用native的方法實現epoll機制喚醒線程,我們就不在深入探討了if(needWake) {? ? ? ? ? ? ? ? nativeWake(mPtr);? ? ? ? ? ? }? ? ? ? }returntrue;? ? }

最后我們再看下Handler 的dispatchMessage方法,這個方法在Looper線程從消息隊列拿出來的時候,通過msg.target.dispatchMessage(msg)調用的。

/**

* Handle system messages here.

*/publicvoiddispatchMessage(Message msg){//優(yōu)先調用callback方法if(msg.callback !=null) {? ? ? ? ? ? handleCallback(msg);? ? ? ? }else{if(mCallback !=null) {if(mCallback.handleMessage(msg)) {return;? ? ? ? ? ? ? ? }? ? ? ? ? ? }//最后會回調我們重寫的handleMessage 方法handleMessage(msg);? ? ? ? }? ? }

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

相關閱讀更多精彩內容

友情鏈接更多精彩內容