? ? ? ? 在一個Android 程序開始運(yùn)行的時候啦辐,會單獨(dú)啟動一個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程序運(yùn)行的時候就被創(chuàng)建肘习,是一個Process當(dāng)中的主線程Main Thread,主要是負(fù)責(zé)控制UI界面的顯示坡倔、更新和控件交互漂佩。在Android程序創(chuàng)建之初,一個Process呈現(xiàn)的是單線程模型罪塔,所有的任務(wù)都在一個線程中運(yùn)行投蝉。因此,我們認(rèn)為征堪,UI Thread所執(zhí)行的每一個函數(shù)瘩缆,所花費(fèi)的時間都應(yīng)該是越短越好。而其他比較費(fèi)時的工作(訪問網(wǎng)絡(luò)佃蚜,下載數(shù)據(jù)庸娱,查詢數(shù)據(jù)庫等)着绊,都應(yīng)該交由子線程去執(zhí)行,以免阻塞主線程熟尉,導(dǎo)致ANR归露。那么問題來了,UI 主線程和子線程是怎么通信的呢斤儿。這就要提到我們這里要講的Handler機(jī)制靶擦。
? ? ? ? ? 簡單來說,handler機(jī)制被引入的目的就是為了實(shí)現(xiàn)線程間通信的雇毫。handler一共干了兩件事:在子線程中發(fā)出message,在主線程中獲取踩蔚、處理message棚放。聽起來好像so easy,如果面試中讓你闡述下Handler機(jī)制馅闽,我們這么回答顯然不是面試官想要的答案飘蚯。我們忽略了一個最重要的問題:子線程何時發(fā)送message,主線程何時獲取處理message福也。
? ? ? ? ? 為了能讓主線程“適時”得處理子線程所發(fā)送的message局骤,顯然只能通過回調(diào)的方式來實(shí)現(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挖诸。其實(shí)還有一個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,因?yàn)閷τ贏ctivity傻唾,Service等系統(tǒng)組件投慈,F(xiàn)rameworks已經(jīng)為我們初始化好了線程(俗稱的UI線程或主線程),在其內(nèi)含有一個Looper冠骄,和由Looper創(chuàng)建的消息隊列逛裤,所以主線程會一直運(yùn)行,處理用戶事件猴抹,直到某些事件(BACK)退出带族。
? ? ? ? ? ?如果,我們需要新建一個線程蟀给,并且這個線程要能夠循環(huán)處理其他線程發(fā)來的消息事件蝙砌,或者需要長期與其他線程進(jìn)行復(fù)雜的交互,這時就需要用到Looper來給線程建立消息隊列跋理。
? ? ? ? ? ?使用Looper也非常的簡單择克,它的方法比較少,最主要的有四個:
? ? ? ? ? ? public static prepare();為線程初始化消息隊列前普。
? ? ? ? ? ? public static myLooper();獲取此Looper對象的引用
? ? ? ? ? ? public static loop();讓線程的消息隊列開始運(yùn)行肚邢,可以接收消息了。
? ? ? ? ? ? public void quit();退出具體哪個Looper
? ? ? ? ? ? 在整個消息處理機(jī)制中,message又叫task骡湖,封裝了任務(wù)攜帶的信息和處理該任務(wù)的handler贱纠,這個很好理解,就不做介紹了响蕴。
? ? ? ? ? ? 說了這么多谆焊,我知道你一定沒聽太明白。沒關(guān)系浦夷,我再對Handler運(yùn)行機(jī)制做個總結(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。好像畫面感不是太強(qiáng)辫继,那我舉個例子吧。試想有一個生產(chǎn)方便面的車間俗慈,這個車間有兩條生產(chǎn)線姑宽,一條是生產(chǎn)面餅的闺阱,一條是生產(chǎn)調(diào)料包的炮车,面餅的生產(chǎn)線比較先進(jìn)一點(diǎn)瘦穆,配備了一個工人叫handler扛或,還配備了一個調(diào)料包分類循環(huán)器碘饼。這個車間能生產(chǎn)很多種方便面艾恼,有老壇酸菜,有泡椒鳳爪舆声,有香菇燉雞柳爽,有紅燒牛肉等等。其中方便面的面餅都是一樣的毙芜,只有調(diào)料包和包裝袋不一樣腋粥,包裝袋是根據(jù)調(diào)料包來定的。那么生產(chǎn)線運(yùn)作起來了:工人Handler把子生產(chǎn)線(子線程)上的調(diào)料包(message)放到了主生產(chǎn)線(主線程)上的調(diào)料包分類循環(huán)器(Looper)上闹瞧,通過輪詢分類篩選后工人Handler確定這是一包合格的老壇酸菜味調(diào)料包奥邮,于是工人把老壇酸菜調(diào)料包和面餅放在一塊(sendmessage)罗珍,告訴主生產(chǎn)線覆旱,這是一包老壇酸菜方便面扣唱,請給這包方便面包一個老壇酸菜的包裝袋(更新UI).
? 好了,我想這下你肯定懂了炼彪。
? ? ? ? ?(如有刊誤正歼,歡迎指正)