Android Handler機(jī)制解析

一蛔屹、Handler消息傳遞機(jī)制初步認(rèn)識(shí):

(一)引入:

????????子線程沒有辦法對(duì)UI界面上的內(nèi)容進(jìn)行操作摹察,如果操作泽谨,將拋出異常:CalledFromWrongThreadException為了實(shí)現(xiàn)子線程中操作UI界面奶躯,Android中引入了Handler消息傳遞機(jī)制帚桩,目的是打破對(duì)主線程的依賴性。

????????handler通俗一點(diǎn)講就是用來在各個(gè)線程之間發(fā)送數(shù)據(jù)的處理對(duì)象嘹黔。在任何線程中账嚎,只要獲得了另一個(gè)線程的handler,則可以通過handler.sendMessage(message)方法向那個(gè)線程發(fā)送數(shù)據(jù)儡蔓」叮基于這個(gè)機(jī)制,我們?cè)谔幚矶嗑€程的時(shí)候可以新建一個(gè)thread喂江,這個(gè)thread擁有UI線程中的一個(gè)handler召锈。當(dāng)thread處理完一些耗時(shí)的操作后通過傳遞過來的handler向UI線程發(fā)送數(shù)據(jù),由UI線程去更新界面获询。?

? ? ? ? 主線程:運(yùn)行所有UI組件涨岁,它通過一個(gè)消息隊(duì)列來完成此任務(wù)。設(shè)備會(huì)將用戶的每項(xiàng)操作轉(zhuǎn)換為消息吉嚣,并將它們放入正在運(yùn)行的消息隊(duì)列中梢薪。主線程位于一個(gè)循環(huán)中,并處理每條消息尝哆。如果任何一個(gè)消息用時(shí)超過5秒沮尿,Android將拋出ANR。所以一個(gè)任務(wù)用時(shí)超過5秒较解,應(yīng)該在一個(gè)獨(dú)立線程中完成它畜疾,或者延遲處理它,當(dāng)主線程空閑下來再返回來處理它印衔。

(二)常用類:(Handler啡捶、Looper、Message奸焙、MessageQueue)

Message:消息瞎暑,其中包含了消息ID彤敛,消息處理對(duì)象以及處理的數(shù)據(jù)等,由MessageQueue統(tǒng)一列隊(duì)了赌,終由Handler處理墨榄。

Handler:處理者,負(fù)責(zé)Message的發(fā)送及處理勿她。使用Handler時(shí)袄秩,需要實(shí)現(xiàn)handleMessage(Message msg)方法來對(duì)特定的Message進(jìn)行處理,例如更新UI等逢并。Handler類的主要作用:(有兩個(gè)主要作用)1)之剧、在工作線程中發(fā)送消息;2)砍聊、在主線程中獲取背稼、并處理消息。

MessageQueue:消息隊(duì)列玻蝌,用來存放Handler發(fā)送過來的消息蟹肘,并按照FIFO規(guī)則執(zhí)行。當(dāng)然俯树,存放Message并非實(shí)際意義的保存帘腹,而是將Message串聯(lián)起來的,等待Looper的抽取聘萨。

Looper:消息泵竹椒,不斷地從MessageQueue中抽取Message執(zhí)行童太。因此米辐,一個(gè)MessageQueue需要一個(gè)Looper。

Thread:線程书释,負(fù)責(zé)調(diào)度整個(gè)消息循環(huán)翘贮,即消息循環(huán)的執(zhí)行場所。

(三)爆惧、Handler狸页、Looper、Message扯再、MessageQueue之間的關(guān)系:

Handler芍耘,Looper和MessageQueue的關(guān)系

Looper和MessageQueue一一對(duì)應(yīng),創(chuàng)建一個(gè)Looper的同時(shí)熄阻,會(huì)創(chuàng)建一個(gè)MessageQueue斋竞;

而Handler與它們的關(guān)系,只是簡單的聚集關(guān)系秃殉,即Handler里會(huì)引用當(dāng)前線程里的特定Looper和MessageQueue坝初;

在一個(gè)線程中浸剩,只能有一個(gè)Looper和MessageQueue,但是可以有多個(gè)Handler鳄袍,而且這些Handler可以共享一個(gè)Looper和MessageQueue绢要;

Message被存放在?MessageQueue中,一個(gè)?MessageQueue中可以包含多個(gè)Message對(duì)象拗小。

【備注】

Looper對(duì)象用來為一個(gè)線程開啟一個(gè)消息循環(huán)重罪,從而操作MessageQueue;

默認(rèn)情況下十籍,Android創(chuàng)建的線程沒有開啟消息循環(huán)Looper蛆封,但是主線程例外。

系統(tǒng)自動(dòng)為主線程創(chuàng)建Looper對(duì)象勾栗,開啟消息循環(huán)惨篱;

所以主線程中使用new來創(chuàng)建Handler對(duì)象。而子線程中不能直接new來創(chuàng)建Handler對(duì)象就會(huì)異常围俘。

子線程中創(chuàng)建Handler對(duì)象砸讳,步驟如下:

Looper.prepare();

Handler handler = new Handler() {

? ? //handlemessage(){}

}

Looper.loop();

(四)、Handler類中常用方法:

handleMessage() ? ?用在主線程中界牡,構(gòu)造Handler對(duì)象時(shí)簿寂,重寫handleMessage()方法。該方法根據(jù)工作線程返回的消息標(biāo)識(shí)宿亡,來分別執(zhí)行不同的操作常遂。

sendEmptyMessage() ? ? 用在工作線程中,發(fā)送空消息挽荠。

sendMessage() ? ? ?用在工作線程中克胳,立即發(fā)送消息。

代碼執(zhí)行順序:sendMessage()-->sendMessageDelay()-->sendMessageAtTime()-->enqueneueMessage().;

(五)圈匆、Message消息類中常用屬性:

arg1 ? ??用來存放整型數(shù)據(jù)

arg2??? ??用來存放整型數(shù)據(jù)

obj??? ? ? 用來存放Object數(shù)據(jù)

what?? ? 用于指定用戶自定義的消息代碼漠另,這樣便于主線程接收后,根據(jù)消息代碼不同而執(zhí)行不同的相應(yīng)操作跃赚。

使用Message需要注意4點(diǎn):

1笆搓、Message雖然也可以通過new來獲取,但是通常使用Message.obtain()或Handler.obtainMessage()方法來從消息池中獲得空消息對(duì)象纬傲,以節(jié)省資源满败;

2、如果一個(gè)Message只需要攜帶簡單的int型數(shù)據(jù)叹括,應(yīng)優(yōu)先使用arg1和arg2屬性來傳遞數(shù)據(jù)算墨,這樣比其他方式節(jié)省內(nèi)存;

3领猾、盡可能使用Message.what來標(biāo)識(shí)信息米同,以便用不同的方式處理Message骇扇;

4、如果需要從工作線程返回很多數(shù)據(jù)信息面粮,可以借助Bundle對(duì)象將這些數(shù)據(jù)集中到一起少孝,然后存放到obj屬性中,再返回到主線程熬苍。

二稍走、Handler、Looper源碼分析:

(一)柴底、Handler的概念:

Handler是用于發(fā)送和處理消息和一個(gè)線程的MessageQueue相關(guān)聯(lián)的Runable對(duì)象婿脸。?

每個(gè)Handler實(shí)例關(guān)聯(lián)到一個(gè)單一線程和線程的messagequeue。?

當(dāng)創(chuàng)建一個(gè)Handler,從你創(chuàng)建它的時(shí)候開始柄驻,它就綁定到創(chuàng)建它的線程以及對(duì)應(yīng)的消息隊(duì)列狐树,handler將發(fā)送消息到消息隊(duì)列,并處理從消息隊(duì)列中取出的消息鸿脓。

Handler的主要用途有兩個(gè):

(1)抑钟、在將來的某個(gè)時(shí)刻執(zhí)行消息或一個(gè)runnable;

(2)野哭、為運(yùn)行在不同線程中的多個(gè)任務(wù)排隊(duì)在塔。

主要依靠以下方法來完成消息調(diào)度:

post(Runnable)、?

postAtTime(Runnable, long)拨黔、?

postDelayed(Runnable, long)蛔溃、?

sendEmptyMessage(int)、?

sendMessage(Message)篱蝇、?

sendMessageAtTime(Message)贺待、?

sendMessageDelayed(Message, long)?

【備注】

post方法是當(dāng)?shù)絉unable對(duì)象到達(dá)就被插入到消息隊(duì)列;?

sendMessage方法允許你把一個(gè)包含有信息的Message插入消息隊(duì)列态兴,它會(huì)在Handler的handlerMessage(Message)方法中執(zhí)行(該方法要求在Handler的子類中實(shí)現(xiàn))狠持。

當(dāng)Handler post或者send消息的時(shí)候疟位,可以在消息隊(duì)列準(zhǔn)備好的時(shí)候立刻執(zhí)行瞻润,或者指定一個(gè)延遲處理或絕對(duì)時(shí)間對(duì)它進(jìn)行處理,后兩個(gè)是實(shí)現(xiàn)了timeout甜刻、ticks或者其他timing-based的行為绍撞。

當(dāng)你的應(yīng)用創(chuàng)建一個(gè)進(jìn)程時(shí),其主線程(UI線程)會(huì)運(yùn)行一個(gè)消息隊(duì)列得院,負(fù)責(zé)管理優(yōu)先級(jí)最高的應(yīng)用程序?qū)ο?Activity傻铣、廣播接收器等)和任何他們創(chuàng)建的windows。你也可以創(chuàng)建自己的線程祥绞,通過handler與主線程進(jìn)行通信非洲,在新創(chuàng)建的線程中handler通過調(diào)用post或sendMessage方法鸭限,將傳入的Runnable或者M(jìn)essage插入到消息隊(duì)列中,并且在適當(dāng)?shù)臅r(shí)候得到處理两踏。?

(二)败京、Handler的用法:

當(dāng)你實(shí)例化一個(gè)Handler的時(shí)候可以使用Callback接口來避免寫自定義的Handler子類。這里的機(jī)制類似與Thread與runable接口的關(guān)系梦染。

在Handler里面赡麦,子類要處理消息的話必須重寫handleMessage()這個(gè)方法,因?yàn)樵趆andler里面它是個(gè)空方法帕识。

(三)泛粹、源碼分析:

A、Handler.java:(3個(gè)屬性肮疗,9個(gè)方法)

3個(gè)屬性:

final?MessageQueue mQueue;

final?Looper mLooper;

final?Callback mCallback;

9個(gè)方法:

public boolean handleMessage(Message msg);?

public final Message obtainMessage()

public final boolean sendMessage(Message msg)

public final boolean sendEmptyMessage(int what)

public final boolean post(Runnable r)

public final boolean postAtTime(Runnable r, long uptimeMillis)

public void dispatchMessage(Message msg)

public boolean sendMessageAtTime(Message msg, long uptimeMillis)

public final boolean sendMessageDelayed(Message msg, long delayMillis)

B晶姊、Looper.JAVA:(4個(gè)屬性,4個(gè)方法)

? ? ? ? 每個(gè)ThreadLocal中只能有一個(gè)Looper伪货,也就是說一個(gè)Thread中只有一個(gè)Looper

4個(gè)屬性:

? ? static final?ThreadLocal<Looper>?sThreadLocal?= new ThreadLocal<Looper>();

? ? final?MessageQueue ?mQueue;

? ? final?Thread ?mThread;

? ? private static?Looper?mMainLooper?= null;

4個(gè)方法:

public static void prepare()

public static void prepareMainLooper()

public static void loop()

public static Looper myLooper()

C帽借、Message.java:(8個(gè)屬性,5個(gè)方法)

8個(gè)屬性:

public int what;

public int arg1;

public int arg2;

public Object obj;

Handler target;?

Message sPool;

int sPoolSize;

int MAX_POOL_SIZE=10;

5個(gè)方法:

public static Message obtain()

public void recycle()

public void setTarget(Handler target)

public Handler getTarget()

public void sendToTarget()


Tips:

MessageQueneue

Handle.sendMessage() -->sendMessageDelay()-->? sendMessageAtTime()

-->enqueneueMessage()(消息加入消息隊(duì)列)

以鏈表的形式超歌,首先進(jìn)來的判空賦值砍艾,next為空,以后進(jìn)來的巍举,根據(jù)delay的時(shí)間判斷脆荷,寫一個(gè)死循環(huán),如果前面的時(shí)間更大就繼續(xù)循環(huán)插到前面去懊悯,直到前面一個(gè)的時(shí)間小于當(dāng)前消息的時(shí)間蜓谋,跳出循環(huán),以這種形式炭分,將隊(duì)列按照延時(shí)時(shí)間排列順序桃焕。

Looper

在子線程中使用handle,new 對(duì)象之前必須? Looper.prepare(); 否則會(huì)報(bào)錯(cuò)“不能在沒有l(wèi)ooper的時(shí)候創(chuàng)建handler”捧毛,(源碼先判斷Looper是否為空观堂,為空拋錯(cuò)),然后后面要Looper.loop();handler與looper是成對(duì)出現(xiàn)的呀忧,主線程new handler不報(bào)錯(cuò)是因?yàn)閼?yīng)用啟動(dòng)的時(shí)候师痕,ActivityThread主線程會(huì)默認(rèn)寫了Looper.prepareMainLooper()

message.Obtain();取消息池里的空閑消息,如果沒有空閑消息再創(chuàng)建message而账,可以復(fù)用

在子線程中調(diào)用handle的發(fā)送消息方法胰坟,處理消息是在創(chuàng)建handle的線程。

looper是跟handler成對(duì)出現(xiàn)的泞辐,利用ThredLocal,ThreadLocalMap以鍵值對(duì)的形式存儲(chǔ)笔横,key為線程對(duì)象Thread.currentThread()能保證唯一竞滓,value為設(shè)置的值。保證唯一性吹缔。

總結(jié)

由sendMessage開始虽界,最后都是調(diào)用enqueueMessage方法,到此為止我們一般都是在新線程調(diào)用涛菠;

接下來Looper類的loop方法會(huì)在當(dāng)前線程(如果是更新UI則在主線程)從MessageQueue中獲取最新消息莉御,通過msg.target.dispatchMessage(msg);調(diào)用到Handler中來,然后調(diào)用到handlemessage(msg)俗冻;

由此礁叔,Handler跨線程最重要的是在當(dāng)前線程(初始化Handler的線程)進(jìn)行l(wèi)oop輪詢,而變量是可以在不同線程訪問的迄薄,所以Handler可以在其他線程向MessageQueue中插入數(shù)據(jù)琅关,而loop則在當(dāng)前線程不斷去取數(shù)據(jù),取得數(shù)據(jù)就回調(diào)讥蔽,達(dá)到跨線程的目的涣易。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市冶伞,隨后出現(xiàn)的幾起案子新症,更是在濱河造成了極大的恐慌,老刑警劉巖响禽,帶你破解...
    沈念sama閱讀 223,126評(píng)論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件徒爹,死亡現(xiàn)場離奇詭異,居然都是意外死亡芋类,警方通過查閱死者的電腦和手機(jī)隆嗅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來侯繁,“玉大人胖喳,你說我怎么就攤上這事≈梗” “怎么了丽焊?”我有些...
    開封第一講書人閱讀 169,941評(píng)論 0 366
  • 文/不壞的土叔 我叫張陵,是天一觀的道長坝锰。 經(jīng)常有香客問我粹懒,道長重付,這世上最難降的妖魔是什么顷级? 我笑而不...
    開封第一講書人閱讀 60,294評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮确垫,結(jié)果婚禮上弓颈,老公的妹妹穿的比我還像新娘帽芽。我一直安慰自己,他們只是感情好翔冀,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,295評(píng)論 6 398
  • 文/花漫 我一把揭開白布导街。 她就那樣靜靜地躺著,像睡著了一般纤子。 火紅的嫁衣襯著肌膚如雪搬瑰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,874評(píng)論 1 314
  • 那天控硼,我揣著相機(jī)與錄音泽论,去河邊找鬼。 笑死卡乾,一個(gè)胖子當(dāng)著我的面吹牛翼悴,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播幔妨,決...
    沈念sama閱讀 41,285評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼鹦赎,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了误堡?” 一聲冷哼從身側(cè)響起古话,我...
    開封第一講書人閱讀 40,249評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎锁施,沒想到半個(gè)月后煞额,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,760評(píng)論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡沾谜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,840評(píng)論 3 343
  • 正文 我和宋清朗相戀三年膊毁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片基跑。...
    茶點(diǎn)故事閱讀 40,973評(píng)論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡婚温,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出媳否,到底是詐尸還是另有隱情栅螟,我是刑警寧澤,帶...
    沈念sama閱讀 36,631評(píng)論 5 351
  • 正文 年R本政府宣布篱竭,位于F島的核電站力图,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏掺逼。R本人自食惡果不足惜吃媒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,315評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧赘那,春花似錦刑桑、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拱礁,卻和暖如春琢锋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背呢灶。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評(píng)論 1 275
  • 我被黑心中介騙來泰國打工吩蔑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人填抬。 一個(gè)月前我還...
    沈念sama閱讀 49,431評(píng)論 3 379
  • 正文 我出身青樓烛芬,卻偏偏與公主長得像,于是被迫代替她去往敵國和親飒责。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赘娄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,982評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 如果一個(gè)成家的女人整日作妖,感覺生活虧欠了你宏蛉,那就生個(gè)小孩獨(dú)自一個(gè)人帶吧遣臼。當(dāng)一個(gè)人承擔(dān)起所有,你才會(huì)刻骨銘心地理解...
    榮我婷婷閱讀 649評(píng)論 1 1
  • 23歲的年齡拾并,沒有13歲小姑涼一整天都無憂無慮和小伙伴到處瘋的歡脫揍堰,也沒有33歲熟女和閨蜜喝著下午茶分享各自感情經(jīng)...
    只會(huì)仰泳的魚閱讀 328評(píng)論 0 0
  • 我家先生有一群發(fā)小十幾年前結(jié)伴來到深圳屏歹。他們有共同的特質(zhì):出身農(nóng)村,初中或中專畢業(yè)之碗,能吃苦有闖勁蝙眶。初到深圳...
    至愛珍寶閱讀 309評(píng)論 0 0