? ? ? ? 在一個Android 程序開始運行的時候,會單獨啟動一個Process。默認(rèn)的情況下,所有這個程序中的Activity,Service,Content Provider,Broadcast Receiver(Android 4大組件)都會跑在這個Process。一個Android 程序默認(rèn)情況下也只有一個Process,但一個Process下卻可以有許多個Thread。在這么多Thread當(dāng)中,有一個Thread,我們稱之為UI Thread。UI Thread在Android程序運行的時候就被創(chuàng)建,是一個Process當(dāng)中的主線程Main Thread,主要是負(fù)責(zé)控制UI界面的顯示、更新和控件交互。在Android程序創(chuàng)建之初,一個Process呈現(xiàn)的是單線程模型,所有的任務(wù)都在一個線程中運行。因此,我們認(rèn)為,UI Thread所執(zhí)行的每一個函數(shù),所花費的時間都應(yīng)該是越短越好。而其他比較費時的工作(訪問網(wǎng)絡(luò),下載數(shù)據(jù),查詢數(shù)據(jù)庫等),都應(yīng)該交由子線程去執(zhí)行,以免阻塞主線程,導(dǎo)致ANR。那么問題來了,UI 主線程和子線程是怎么通信的呢。這就要提到我們這里要講的Handler機制。
? ? ? ? ? 簡單來說,handler機制被引入的目的就是為了實現(xiàn)線程間通信的。handler一共干了兩件事:在子線程中發(fā)出message,在主線程中獲取、處理message。聽起來好像so easy,如果面試中讓你闡述下Handler機制,我們這么回答顯然不是面試官想要的答案。我們忽略了一個最重要的問題:子線程何時發(fā)送message,主線程何時獲取處理message。
? ? ? ? ? 為了能讓主線程“適時”得處理子線程所發(fā)送的message,顯然只能通過回調(diào)的方式來實現(xiàn)——開發(fā)者只要重寫Handler類中處理消息的方法,當(dāng)子線程發(fā)送消時,Handler類中處理消息的方法就會被自動回調(diào)。
? ? ? ? ? ?Handler類包含如下方法用于發(fā)送處理消息
? ? ? ? ? ?void handleMessage(Message msg):處理消息的方法,該方法通常用于被重寫。
? ? ? ? ? ?final boolean hasMessage(int what):檢查消息隊列中是否包含what屬性為指定值的消息。
? ? ? ? ? ?final boolean hasMessage(int what,Object object):檢查消息隊列中是否包含what屬性為指定值且object屬性為指定對象的消息。
? ? ? ? ? ?sendEmptyMessage(int what)發(fā)送空消息。
? ? ? ? ? ?sendEmptyMessageDelayed(int what,longdelayMillis);指定多少毫秒之后發(fā)送空消息。
? ? ? ? ? ?sendMessage(Message msg)立即發(fā)送消息。
? ? ? ? ? ?sendMessageDelayed(int what,longdelayMillis);指定多少毫秒之后發(fā)送消息。
? ? ? ? ? ?借助以上方法,我們就可以自由穿梭于主線程和子線程之中了。
? ? ? ? ? ?到這里就結(jié)束了么?當(dāng)然沒有。我要講的東西才剛剛開始,要知道消息處理這件事,不是handler一個人在戰(zhàn)斗,android的消息處理有三個核心類:Handler,Looper,和Message。其實還有一個MessageQueue(消息隊列),但是Message Queue被封裝到Looper里面了,我們不會直接與Message Queue打交道。
? ? ? ? ? ?Looper的字面意思是“循環(huán)裝置”,它被設(shè)計用來使一個普通線程變成Looper線程。所謂Looper線程就是循環(huán)工作的線程。在程序開發(fā)中,我們經(jīng)常會需要一個線程不斷循環(huán),一旦有新任務(wù)則執(zhí)行,執(zhí)行完繼續(xù)等待下一個任務(wù),這就是Looper線程。Looper是用于給一個線程添加一個消息隊列(MessageQueue),并且循環(huán)等待,當(dāng)有消息時會喚起線程來處理消息的一個工具,直到線程結(jié)束為止。通常情況下不會用到Looper,因為對于Activity,Service等系統(tǒng)組件,F(xiàn)rameworks已經(jīng)為我們初始化好了線程(俗稱的UI線程或主線程),在其內(nèi)含有一個Looper,和由Looper創(chuàng)建的消息隊列,所以主線程會一直運行,處理用戶事件,直到某些事件(BACK)退出。
? ? ? ? ? ?如果,我們需要新建一個線程,并且這個線程要能夠循環(huán)處理其他線程發(fā)來的消息事件,或者需要長期與其他線程進行復(fù)雜的交互,這時就需要用到Looper來給線程建立消息隊列。
? ? ? ? ? ?使用Looper也非常的簡單,它的方法比較少,最主要的有四個:
? ? ? ? ? ? public static prepare();為線程初始化消息隊列。
? ? ? ? ? ? public static myLooper();獲取此Looper對象的引用
? ? ? ? ? ? public static loop();讓線程的消息隊列開始運行,可以接收消息了。
? ? ? ? ? ? public void quit();退出具體哪個Looper
? ? ? ? ? ? 在整個消息處理機制中,message又叫task,封裝了任務(wù)攜帶的信息和處理該任務(wù)的handler,這個很好理解,就不做介紹了。
? ? ? ? ? ? 說了這么多,我知道你一定沒聽太明白。沒關(guān)系,我再對Handler運行機制做個總結(jié):
? ? ? ? ? ?子線程(無looper)借用主線程(有l(wèi)ooper)里的Hander發(fā)送一條message到主線程,這個message會被主線程放入message queue,主線程里面有一個looper,在輪詢message queue的時候發(fā)現(xiàn)有一條message。調(diào)用handler消息處理者,執(zhí)行handlemessage方法,去處理這個message,就可以在handlemessage的方法里面更新ui。好像畫面感不是太強,那我舉個例子吧。試想有一個生產(chǎn)方便面的車間,這個車間有兩條生產(chǎn)線,一條是生產(chǎn)面餅的,一條是生產(chǎn)調(diào)料包的,面餅的生產(chǎn)線比較先進一點,配備了一個工人叫handler,還配備了一個調(diào)料包分類循環(huán)器。這個車間能生產(chǎn)很多種方便面,有老壇酸菜,有泡椒鳳爪,有香菇燉雞,有紅燒牛肉等等。其中方便面的面餅都是一樣的,只有調(diào)料包和包裝袋不一樣,包裝袋是根據(jù)調(diào)料包來定的。那么生產(chǎn)線運作起來了:工人Handler把子生產(chǎn)線(子線程)上的調(diào)料包(message)放到了主生產(chǎn)線(主線程)上的調(diào)料包分類循環(huán)器(Looper)上,通過輪詢分類篩選后工人Handler確定這是一包合格的老壇酸菜味調(diào)料包,于是工人把老壇酸菜調(diào)料包和面餅放在一塊(sendmessage),告訴主生產(chǎn)線,這是一包老壇酸菜方便面,請給這包方便面包一個老壇酸菜的包裝袋(更新UI).
? 好了,我想這下你肯定懂了。
? ? ? ? ?(如有刊誤,歡迎指正)