Android消息機(jī)制字典型探究(二)
本文原創(chuàng),轉(zhuǎn)載請(qǐng)經(jīng)過本人準(zhǔn)許
寫在前面:
為了完成整個(gè)Android消息機(jī)制的探究缝龄,我準(zhǔn)備將知識(shí)點(diǎn)細(xì)分成一個(gè)個(gè)模塊。在連載的第一篇文章中挂谍,在子線程更新UI導(dǎo)致崩潰叔壤,我們?nèi)シ治鎏骄苛薃ndroid中不允許子線程更新UI的原因,是由于線程安全的問題口叙。
當(dāng)然我們目前分析的東西和寫出文字都與Android消息機(jī)制無關(guān)炼绘。不過我其實(shí)是想給大家展示學(xué)習(xí)編程,或者說學(xué)習(xí)Android的一些好的習(xí)慣和解決問題的思路妄田,總結(jié)起來就是:實(shí)踐去發(fā)現(xiàn)問題俺亮,全面的理解問題,尋找最優(yōu)解疟呐。Android本身就是一個(gè)復(fù)雜而有機(jī)的整體脚曾,由一個(gè)知識(shí)點(diǎn)可以牽出一條知識(shí)線。從而構(gòu)成相關(guān)的知識(shí)體系启具。
這種學(xué)習(xí)方式會(huì)讓你知道的越來越多本讥,也能站在一定的高度上體會(huì)Android在設(shè)計(jì)之時(shí)的巧妙,全局的理解Android,做到融會(huì)貫通拷沸。也能在你的代碼中色查,收獲很多有益的啟發(fā)。
說完以上這些撞芍,就可以正式開始本文的話題了秧了。既然在我們只可以在主線程更新UI,那解決這個(gè)問題勤庐,一共有幾種方式呢示惊?我就來直接告訴大家,解決子線程更新UI問題的方式愉镰,一共有三種米罚。
-
runOnUiThread
簡單講解一下代碼,點(diǎn)擊crash按鈕丈探,開啟一個(gè)子線程录择,顯然我們?cè)谧泳€程中直接給 iv_handler
設(shè)置一張圖片,是肯定崩潰的碗降。當(dāng)我們調(diào)用runOnUiThread方法隘竭,并且傳入一個(gè)Runnable對(duì)象,并且在其中設(shè)置更新UI的邏輯讼渊,問題就解決了动看。相信你也和我一樣對(duì)此非常好奇,那就趕緊點(diǎn)進(jìn)去看看爪幻,源碼中是如何實(shí)現(xiàn)的吧菱皆!先來翻譯一下這段注釋:
在UI線程中執(zhí)行該Runnable. * 如果當(dāng)前線程是UI線程,那么該Runnable會(huì)立即執(zhí)行. * 如果當(dāng)前的線程不是UI線程則調(diào)用UI線程handler的post()方法將其放入U(xiǎn)I線程的消息隊(duì)列中. *
注意:勿在runOnUiThread(Runnable runnable)中做耗時(shí)操作首先我想說明的是,runOnUiThread方法是屬于Activity的挨稿,也就是說我們能拿到Activity才能使用該方法仇轻。我們本文的這個(gè)例子,明顯是執(zhí)行了mHandler.post(action)方法奶甘。我們目前不去研究handler.post方法篷店,因?yàn)橐粫?huì)你就知道為什么了。再來看看第二種解決問題的辦法臭家。
-
view.post
代碼跟上一個(gè)解決辦法如出一轍疲陕,我們還是來看看源碼,分析一下這個(gè)方法的實(shí)現(xiàn)方式钉赁。
先來翻譯一下注釋的意思:
將Runnable對(duì)象添加到message queue中鸭轮,并且這個(gè)runnable對(duì)象會(huì)跑在UI線程中。
翻譯完注釋再來看代碼橄霉,當(dāng)View和Activity完成attach操作時(shí),會(huì)產(chǎn)生一個(gè)attachInfo參數(shù),在attachInfo參數(shù)中取出來了屬于activity的mHandler姓蜂,仍然去調(diào)用了mHandler.post(action)方法按厘。也就是說無論我們是選擇第一種方法還是第二種方法去解決這個(gè)崩潰問題,都是殊途同歸的钱慢,最后經(jīng)過層層封裝逮京,都走到了handler.post方法中。
handler.post
啊哈哈束莫,我們連載通篇的主角Handler今天終于正式登場(chǎng)了懒棉,沒錯(cuò)上面的代碼就是將子線程的消息發(fā)送到主線程并處理的標(biāo)準(zhǔn)寫法。等等览绿,post方法在哪里策严?別著急,在本篇文章中饿敲,我并不打算給大家展開整個(gè)Handler知識(shí)體系的研究妻导。我們先來看看post方法調(diào)用層級(jí)
可以看到,方法的調(diào)用順序?yàn)閜ost--sendMessageDelayed--sendMessageAtTime.
在本文中我們不再繼續(xù)深究下去怀各,未來我會(huì)對(duì)這些方法的調(diào)用層級(jí)倔韭、順序、以及使用場(chǎng)景為大家進(jìn)行一下完整地整理總結(jié)瓢对,本文不再贅述寿酌。
想強(qiáng)調(diào)的是:
其實(shí)在這篇博客的目的:
1.從解決問題并尋找方法的角度引出Handler,真正開啟Handler的知識(shí)體系硕蛹。
2.思考問題醇疼,解決問題的過程〖嗣溃回首第一篇文章中僵腺,我因?yàn)樵谧泳€程更新UI而造成了崩潰,然后:
在尋找解決辦法之前壶栋,帶著強(qiáng)烈的好奇心辰如,去尋找了為何不能在子線程更新UI原因。在這個(gè)過程中贵试,我理解了什么線程安全琉兜,深入理解了Context。
去尋找解決問題的所有辦法(三種)毙玻,并且去探究了這幾種方法的原理豌蟋,試圖選擇在本例中的最優(yōu)解(事實(shí)上這幾種方法本質(zhì)上沒區(qū)別)。
將問題簡單化桑滩,所有問題的解決辦法都指向了Handler梧疲,所以我們只需要探究Handler即可。
寫在最后:
很多初學(xué)者認(rèn)為Handler就是為了解決子線程更新UI的問題而存在的,事實(shí)上這種理解是錯(cuò)誤的幌氮。Handler作為Android的線程間通信的機(jī)制缭受,意義遠(yuǎn)不止此。