)
在Android開(kāi)發(fā)中主線程扮演著至關(guān)重要的角色。毫不夸張的說(shuō)它就相當(dāng)于Android的心臟。只要它還在跳動(dòng)的運(yùn)行Android應(yīng)用就不會(huì)終止。它負(fù)責(zé)處理UI事件、界面更新、以及與用戶交互的各種操作。本文將深入分析Android主線程的原理、獨(dú)特機(jī)制以及應(yīng)用為開(kāi)發(fā)者提供全面的了解和掌握主線程的知識(shí)。主線程的原理Android應(yīng)用的核心原則之一是單線程模型也就是說(shuō)大多數(shù)與用戶界面相關(guān)的操作都必須在主線程中執(zhí)行。這一原則的背后是Android操作系統(tǒng)的設(shè)計(jì)主要有以下幾個(gè)原因UI一致性在單線程模型下UI操作不會(huì)被多線程競(jìng)爭(zhēng)導(dǎo)致的不一致性問(wèn)題確保了用戶界面的穩(wěn)定性和一致性。性能優(yōu)化單線程模型簡(jiǎn)化了線程管理降低了多線程帶來(lái)的復(fù)雜性有助于提高應(yīng)用性能。安全性通過(guò)將UI操作限制在主線程可以減少因多線程競(jìng)爭(zhēng)而引發(fā)的潛在問(wèn)題如死鎖和競(jìng)爭(zhēng)條件。主線程的原理可以用以下偽代碼表示publicclassMainThread{publicstaticvoidmain(String[]args){// 初始化應(yīng)用Application appcreateApplication();// 創(chuàng)建主線程消息循環(huán)Looper.prepareMainLooper();// 啟動(dòng)主線程while(true){Message msgLooper.getMainLooper().getNextMessage();if(msg!null){// 處理消息app.handleMessage(msg);}}}}在上述偽代碼中主線程通過(guò)消息循環(huán)Message Loop來(lái)不斷處理消息這些消息通常包括UI事件、定時(shí)任務(wù)等。應(yīng)用的UI操作都會(huì)被封裝成消息然后由主線程依次處理。主線程的獨(dú)特機(jī)制主線程有一些獨(dú)特的機(jī)制其中最重要的是消息隊(duì)列Message Queue和Handler。消息隊(duì)列Message Queue消息隊(duì)列是主線程用來(lái)存儲(chǔ)待處理消息的數(shù)據(jù)結(jié)構(gòu)。每個(gè)消息都有一個(gè)與之相關(guān)的Handler它負(fù)責(zé)將消息放入隊(duì)列中然后由主線程依次處理。消息隊(duì)列的機(jī)制確保了消息的有序性和及時(shí)性。publicMessagenext(){final long ptrmPtr;if(ptr0){returnnull;}int pendingIdleHandlerCount-1;int nextPollTimeoutMillis0;for(;;){if(nextPollTimeoutMillis!0){Binder.flushPendingCommands();}nativePollOnce(ptr,nextPollTimeoutMillis);synchronized(this){final long nowSystemClock.uptimeMillis();Message prevMsgnull;Message msgmMessages;if(msg!nullmsg.targetnull){do{prevMsgmsg;msgmsg.next;}while(msg!null!msg.isAsynchronous());}if(msg!null){if(nowmsg.when){nextPollTimeoutMillis(int)Math.min(msg.when-now,Integer.MAX_VALUE);}else{// Got a message.mBlockedfalse;if(prevMsg!null){prevMsg.nextmsg.next;}else{mMessagesmsg.next;}msg.nextnull;msg.markInUse();returnmsg;}}else{nextPollTimeoutMillis-1;}...}...}}HandlerHandler是一個(gè)與特定線程關(guān)聯(lián)的對(duì)象它可以用來(lái)發(fā)送和處理消息。在主線程中通常使用new Handler(Looper.getMainLooper())來(lái)創(chuàng)建一個(gè)與主線程關(guān)聯(lián)的Handler。開(kāi)發(fā)者可以使用Handler來(lái)將任務(wù)提交到主線程的消息隊(duì)列中。Handler handlernewHandler(Looper.getMainLooper());handler.post(newRunnable(){Overridepublicvoidrun(){// 在主線程執(zhí)行}});同步屏障在Android中消息可以分為同步消息和異步消息。通常我們發(fā)送的消息都是同步消息。然而有一種特殊情況即開(kāi)啟同步屏障。同步屏障是一種消息機(jī)制的特性可以阻止同步消息的處理只允許異步消息通過(guò)。通過(guò)調(diào)用MessageQueue的postSyncBarrier()方法可以開(kāi)啟同步屏障。在開(kāi)啟同步屏障后發(fā)送的這條消息它的target為null。privateintpostSyncBarrier(long when){synchronized(this){final int tokenmNextBarrierToken;final Message msgMessage.obtain();msg.markInUse();// 沒(méi)有設(shè)置targettarget為nullmsg.whenwhen;msg.arg1token;Message prevnull;Message pmMessages;if(when!0){while(p!nullp.whenwhen){prevp;pp.next;}}if(prev!null){// invariant: p prev.nextmsg.nextp;prev.nextmsg;}else{msg.nextp;mMessagesmsg;}returntoken;}}那么開(kāi)啟同步屏障后所謂的異步消息又是如何被處理的呢我們又可以回到之前MessageQueue中的next方法了publicMessagenext(){// 省略部分代碼只體現(xiàn)出來(lái)同步屏障的代碼...for(;;){...synchronized(this){final long nowSystemClock.uptimeMillis();Message prevMsgnull;Message msgmMessages;//注意這里開(kāi)始出來(lái)同步屏障//如果targetnull認(rèn)為它就是屏障進(jìn)行循環(huán)遍歷直到找到第一個(gè)異步的消息if(msg!nullmsg.targetnull){do{prevMsgmsg;msgmsg.next;}while(msg!null!msg.isAsynchronous());}...}...}}所以同步屏障是會(huì)讓消息順序進(jìn)行調(diào)整讓其忽略現(xiàn)有的同步消息來(lái)直接處理臨近的異步消息?,F(xiàn)在聽(tīng)起來(lái)已經(jīng)知道了同步屏障的作用但它的實(shí)際應(yīng)用又有哪些呢應(yīng)用場(chǎng)景雖然在日常應(yīng)用開(kāi)發(fā)中同步屏障的使用頻率較低但在Android系統(tǒng)源碼中同步屏障的使用場(chǎng)景非常重要。一個(gè)典型的使用場(chǎng)景是在UI更新時(shí)例如在View的繪制、布局調(diào)整、刷新等操作中系統(tǒng)會(huì)開(kāi)啟同步屏障以確保與UI相關(guān)的異步消息得到優(yōu)先處理。當(dāng)UI更新完成后同步屏障會(huì)被移除允許后續(xù)的同步消息得以處理。對(duì)應(yīng)的是ViewRootImpl#scheduleTraversals()voidscheduleTraversals(){if(!mTraversalScheduled){mTraversalScheduledtrue;// 設(shè)置同步屏障mTraversalBarriermHandler.getLooper().getQueue().postSyncBarrier();mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL,mTraversalRunnable,null);notifyRendererOfFramePending();pokeDrawLockIfNeeded();}}voidunscheduleTraversals(){if(mTraversalScheduled){mTraversalScheduledfalse;// 移除同步屏障mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);mChoreographer.removeCallbacks(Choreographer.CALLBACK_TRAVERSAL,mTraversalRunnable,null);}}經(jīng)典問(wèn)題Android 主線程的消息循環(huán)是通過(guò)Looper和Handler來(lái)實(shí)現(xiàn)的。以下是一段偽代碼示例// 創(chuàng)建一個(gè) Looper關(guān)聯(lián)當(dāng)前線程Looper.prepare();Looper loopLooper.myLooper();// 創(chuàng)建一個(gè) Handler它將和當(dāng)前 Looper 關(guān)聯(lián)Handler handlernewHandler();// 進(jìn)入消息循環(huán)Looper.loop();開(kāi)啟loop后的核心代碼如下publicstaticvoidloop(){final Looper memyLooper();...for(;;){if(!loopOnce(me,ident,thresholdOverride)){return;}}}privatestaticbooleanloopOnce(final Looper me,final long ident,final int thresholdOverride){// 注意沒(méi)消息會(huì)被阻塞進(jìn)入休眠狀態(tài)Message msgme.mQueue.next();// might blockif(msgnull){// No message indicates that the message queue is quitting.returnfalse;}...try{msg.target.dispatchMessage(msg);if(observer!null){observer.messageDispatched(token,msg);}dispatchEndneedEndTime?SystemClock.uptimeMillis():0;}catch(Exception exception){if(observer!null){observer.dispatchingThrewException(token,msg,exception);}throwexception;}finally{ThreadLocalWorkSource.restore(origWorkSource);if(traceTag!0){Trace.traceEnd(traceTag);}}...msg.recycleUnchecked();returntrue;}在這段示例中主線程的消息循環(huán)被啟動(dòng)它會(huì)等待來(lái)自消息隊(duì)列的消息。有了這個(gè)基礎(chǔ)下面的問(wèn)題就簡(jiǎn)單了為什么主線程不會(huì)陷入無(wú)限循環(huán)主線程的消息循環(huán)不會(huì)陷入無(wú)限循環(huán)因?yàn)樗粩嗟貜南㈥?duì)列中獲取消息并處理它們。如果沒(méi)有消息要處理消息循環(huán)會(huì)進(jìn)入休眠狀態(tài)不會(huì)持續(xù)消耗 CPU 資源。只有在有新消息到達(dá)時(shí)主線程才會(huì)被喚醒來(lái)處理這些消息。這個(gè)機(jī)制確保主線程能夠響應(yīng)用戶的操作而不陷入死循環(huán)。如果沒(méi)有消息主線程會(huì)如何處理如果消息隊(duì)列為空主線程的消息循環(huán)會(huì)等待直到有新消息到達(dá)。在等待期間它不會(huì)執(zhí)行任何操作也不會(huì)陷入循環(huán)。這是因?yàn)?Android 的消息循環(huán)是基于事件驅(qū)動(dòng)的只有當(dāng)有事件消息到達(dá)時(shí)才會(huì)觸發(fā)主線程執(zhí)行相應(yīng)的處理代碼。當(dāng)新消息被投遞到消息隊(duì)列后主線程會(huì)被喚醒執(zhí)行相應(yīng)的處理操作然后再次進(jìn)入等待狀態(tài)。這種事件驅(qū)動(dòng)的消息循環(huán)機(jī)制使得 Android 應(yīng)用能夠高效地管理用戶交互和異步操作同時(shí)保持了響應(yīng)性和低能耗。所以主線程不會(huì)陷入無(wú)限循環(huán)而是在需要處理事件時(shí)才會(huì)執(zhí)行相應(yīng)的代碼。結(jié)論Android主線程是應(yīng)用的核心負(fù)責(zé)處理UI事件、界面更新和定時(shí)任務(wù)等。了解主線程的原理和獨(dú)特機(jī)制是Android開(kāi)發(fā)的關(guān)鍵它有助于確保應(yīng)用的穩(wěn)定性和性能。通過(guò)消息隊(duì)列和Handler開(kāi)發(fā)者可以在主線程中安全地處理各種任務(wù)提供流暢的用戶體驗(yàn)。最后大家分享一份全套的Android學(xué)習(xí)資料給那些想學(xué)習(xí) Android 的小伙伴們一點(diǎn)幫助適用于任何想學(xué)習(xí)Android開(kāi)發(fā)但不知道從哪里開(kāi)始的人也適用于已經(jīng)開(kāi)始進(jìn)行Android開(kāi)發(fā)但想要變得更好的任何人一、Android所有方向的學(xué)習(xí)路線為了成為更好的 Android 開(kāi)發(fā)者這里為大家提供了總的路線圖。它的用處就在于你可以按照上面的知識(shí)點(diǎn)去找對(duì)應(yīng)的學(xué)習(xí)資源保證自己學(xué)得較為全面。如果下面這個(gè)學(xué)習(xí)路線能幫助大家成為一個(gè)更好的 Android 開(kāi)發(fā)者那么我的使命也就完成了包括Android應(yīng)用開(kāi)發(fā)、系統(tǒng)開(kāi)發(fā)、音視頻開(kāi)發(fā)、Flutter開(kāi)發(fā)、小程序開(kāi)發(fā)、UI界面、車載系統(tǒng)開(kāi)發(fā)等等二、學(xué)習(xí)軟件工欲善其事必先利其器。學(xué)習(xí)Android常用的Android Studio視頻教程和Android Studio最新的安裝包都在這里了給大家節(jié)省了很多時(shí)間。三、進(jìn)階學(xué)習(xí)視頻我們?cè)趯W(xué)習(xí)的時(shí)候往往書籍源碼難以理解閱讀困難這時(shí)候視頻教程教程是就很適合了生動(dòng)形象加上案例實(shí)戰(zhàn)科學(xué)有趣才能更方便的學(xué)習(xí)下去。四、實(shí)戰(zhàn)案例光學(xué)理論是沒(méi)用的要學(xué)會(huì)跟著一起敲要?jiǎng)邮謱?shí)操才能將自己的所學(xué)運(yùn)用到實(shí)際當(dāng)中去這時(shí)候可以搞點(diǎn)實(shí)戰(zhàn)案例來(lái)學(xué)習(xí)。五、經(jīng)典書籍閱讀閱讀Android經(jīng)典書籍可以幫助讀者提高技術(shù)水平開(kāi)拓視野掌握核心技術(shù)提高解決問(wèn)題的能力同時(shí)也可以借鑒他人的經(jīng)驗(yàn)。對(duì)于想要深入學(xué)習(xí)Android開(kāi)發(fā)的讀者來(lái)說(shuō)閱讀Android經(jīng)典書籍是非常有必要的。六、面試資料我們學(xué)習(xí)Android必然是為了找到高薪的工作下面這些面試題是來(lái)自阿里、騰訊、字節(jié)等一線互聯(lián)網(wǎng)大廠最新的面試資料并且有阿里大佬給出了權(quán)威的解答刷完這一套面試資料相信大家都能找到滿意的工作。這份完整版的Android全套學(xué)習(xí)資料已經(jīng)上傳CSDN朋友們?nèi)绻枰梢晕⑿艗呙柘路紺SDN官方認(rèn)證二維碼免費(fèi)領(lǐng)取【保證100%免費(fèi)】