從頂層設(shè)計(jì)的角度對(duì)Android ANR機(jī)制的一些思考

“不能在子線程中更新UI”
“主線程不能做耗時(shí)操作”

這些話被我們奉為圭臬敢茁,但有多少人想過(guò)為什么不能在子線程中更新UI?為什么主線程不能做耗時(shí)操作改淑?

首先先要從Android中的一個(gè)叫ANR的機(jī)制一點(diǎn)一點(diǎn)說(shuō)起!

在說(shuō)ANR之前 我們還是先要了解一下什么是ANR

ANR全名:Application Not Responding 即“應(yīng)用程序無(wú)響應(yīng)”。首先我們嘗試去Android Developers官網(wǎng)尋找一下對(duì)于ANR的權(quán)威解釋


Android開(kāi)發(fā)者官網(wǎng)截圖

這是Android開(kāi)發(fā)者官網(wǎng)開(kāi)發(fā)者指南中對(duì)ANR以及觸發(fā)機(jī)制的描述, 文檔第一段可以看到這句話:

"系統(tǒng)會(huì)通過(guò)顯示一個(gè)說(shuō)明您的應(yīng)用已停止的響應(yīng)的對(duì)話框來(lái)防范一段時(shí)間內(nèi)的響應(yīng)不足的應(yīng)用程序”壁袄。

讀起來(lái)雖然很晦澀...但我們還是會(huì)有一個(gè)疑問(wèn):文中所說(shuō)的防范 具體是防范哪些應(yīng)用程序? 該應(yīng)用程序又是執(zhí)行在哪個(gè)線程?媚媒。不急嗜逻,我們嘗試從第二段文檔中找下答案,注意這句話:

“在您的應(yīng)用程序執(zhí)行可能冗長(zhǎng)的操作的任何情況下缭召, 您不應(yīng)該在UI線程上執(zhí)行工作”栈顷。

其中您不應(yīng)該在UI線程上執(zhí)行工作這句話還被黑體加粗逆日。

由此,結(jié)合文檔我們大致明白萄凤,之前提到的“防范的應(yīng)用程序”指的是耗時(shí)操作室抽,“執(zhí)行的線程“是UI線程。再結(jié)合第二段文檔中的話可以得出一個(gè)結(jié)論:當(dāng)你在UI線程當(dāng)中執(zhí)行耗時(shí)操作的時(shí)候蛙卤,會(huì)觸發(fā)Android防范機(jī)制:ANR狠半。

那么今天我們討論的重點(diǎn)并不在于此,今天我們要從設(shè)計(jì)師緯度去分析:

為什么要有ANR機(jī)制以及ANR機(jī)制在Android程序中存在的必要性

那好颤难,我們根據(jù)開(kāi)發(fā)經(jīng)驗(yàn) 大膽想象一下是否可以把App的代碼宏觀上分為兩種類型:
一種是 UI操作(即時(shí)反饋)
UI操作(包括但不限于):

  • 界面的渲染
  • View的綁定神年,刷新
  • 動(dòng)畫(huà)

一種是 業(yè)務(wù)邏輯(耗時(shí)操作)
業(yè)務(wù)邏輯相關(guān)(包括但不限于):

  • 網(wǎng)絡(luò)數(shù)據(jù)的收發(fā)和上傳
  • 數(shù)據(jù)庫(kù)的CRUD操作
  • IO的讀寫(xiě)


    圖片

    如圖,現(xiàn)在我們做一個(gè)假設(shè):假設(shè)我們的這些代碼共用一個(gè)線程行嗤,會(huì)發(fā)生什么問(wèn)題呢已日?

首先 默認(rèn)情況下代碼執(zhí)行規(guī)則是從左至右,從上至下同步執(zhí)行栅屏,那么如果有耗時(shí)操作飘千,代碼就會(huì)阻塞。反饋到界面上最直觀的感受就是響應(yīng)延遲卡頓栈雳,如果你代碼書(shū)寫(xiě)順序顛倒 控件拿不到數(shù)據(jù)护奈,還會(huì)報(bào)出空指針等等的異常。用戶體驗(yàn)不能用糟糕形容哥纫,簡(jiǎn)直就是毫無(wú)用戶體驗(yàn)霉旗。

顯然 谷歌工程師不會(huì)如此愚蠢。工程師們?yōu)榱艘?guī)避這個(gè)問(wèn)題蛀骇,這就要引入Android中一個(gè)重要概念叫做:異步

異步是目的厌秒,想實(shí)現(xiàn)這個(gè)目的就需要用到多線程,那么多線程狀態(tài)下Android的代碼又是如何去執(zhí)行擅憔?

首先我們要知道多線程執(zhí)行的本質(zhì)是:CPU在多條線程之間做快速的切換鸵闪,它是隨機(jī)并發(fā)執(zhí)行

我們的代碼要想在這種環(huán)境下異步執(zhí)行會(huì)面臨以下問(wèn)題:

  • UI操作的同步問(wèn)題
  • UI操作(View)的不可預(yù)期性

解釋一下這些問(wèn)題,我們知道UI操作是即時(shí)反饋


模擬線程執(zhí)行任務(wù)

如圖 我們假設(shè)有三條線程暑诸,線程一給控件賦值蚌讼,線程一的控件值依賴于線程二網(wǎng)絡(luò)請(qǐng)求拿到的數(shù)據(jù),線程二的控件賦值又依賴于線程三中數(shù)據(jù)庫(kù)查詢到的值个榕,請(qǐng)問(wèn)我如何確保多線程并發(fā)訪問(wèn)時(shí)一定先執(zhí)行線程三 再執(zhí)行線程二 最后執(zhí)行線程一呢啦逆?換句話說(shuō)我如何解決同步問(wèn)題?

Ok 肯定有小伙伴說(shuō) 想那么復(fù)雜干嘛笛洛,加個(gè)鎖不就完事兒了嘛...加鎖是能保證線程安全,互斥乃坤,這當(dāng)然沒(méi)問(wèn)題苛让, 重點(diǎn)是沟蔑,你如何確定加鎖的位置?如果盲目加鎖還可能會(huì)讓控件(View)處于一個(gè)不可預(yù)期的狀態(tài)狱杰,一不小心還可能造成多線程死鎖的現(xiàn)象瘦材。

既然無(wú)法用代碼的方式去解決這個(gè)問(wèn)題,那我們就做減法仿畸,從書(shū)寫(xiě)方式上尋找突破點(diǎn) 既然不能加鎖也不能讓線程同步食棕,那我們就索性把UI操作單拎出來(lái),你們玩你們的错沽,事成之后告訴我一個(gè)結(jié)果就行簿晓,控件不就是想要這么一個(gè)結(jié)果渲染界面嘛。

按著這個(gè)思路千埃,其實(shí)這個(gè)問(wèn)題很好解決 我們只需要在編寫(xiě)代碼的時(shí)候遵循一個(gè)規(guī)則就可以完全規(guī)避Android中UI操作在多線程異步之間的沖突憔儿。

這個(gè)規(guī)則就是:只要是涉及到UI操作的代碼,我們都單獨(dú)的放到一個(gè)線程中放可,這個(gè)存放UI的線程要想滿足UI控件的正常工作必須要滿足:線程非安全谒臼,不能加鎖,不能阻塞耀里!

到此 這個(gè)存放UI的線程想必大家都知道了蜈缤,對(duì)!沒(méi)錯(cuò)冯挎!就是我們口口相傳大名鼎鼎的:"主線程"又稱之為UI線程底哥。

Android單線程模式

這也就回答了開(kāi)頭"為什么不能在子線程中更新UI?為什么主線程不能做耗時(shí)操作"的迷之疑問(wèn)...但似乎有相當(dāng)一部分開(kāi)發(fā)者只是機(jī)械型的遵循這一規(guī)則织堂。

谷歌為了最大化提升用戶體驗(yàn) 讓開(kāi)發(fā)者都遵守這個(gè)規(guī)則叠艳,保證規(guī)則的良性循環(huán) ,ANR機(jī)制就誕生了易阳,Android在主線程之間會(huì)設(shè)置一個(gè)5s——20s不等的時(shí)間閥值(產(chǎn)生ANR的上下文不同附较,超時(shí)時(shí)間也會(huì)不同),如果主線程中的程序運(yùn)行/阻塞的時(shí)間超出了這個(gè)閥值潦俺,就會(huì)拋出ANR異常拒课,如下圖


開(kāi)發(fā)者官網(wǎng)截圖

所以這也就是Android為什么會(huì)有ANR這么一個(gè)機(jī)制,ANR的必要性也由此顯現(xiàn)事示。

掌握原理及其設(shè)計(jì)思想之后 寫(xiě)起代碼來(lái)才能舉一反三更加得心應(yīng)手早像,不會(huì)再為某一個(gè)莫名其妙的bug頭疼半天,但不得不說(shuō) 谷歌工程師代碼設(shè)計(jì)的還是非常之精妙的肖爵,其中的思路非常寶貴值得我們借鑒卢鹦。

但以上僅是個(gè)人對(duì)ANR機(jī)制狹義上的理解(也算是自問(wèn)自答式的圈地自嗨?劝堪?冀自?哈哈)揉稚,旨在集思廣益交流分享,如有不同觀點(diǎn)非常歡迎與鄙人探討學(xué)習(xí)熬粗,如果此文章對(duì)你或多或少有些啟發(fā)搀玖,那就點(diǎn)個(gè)愛(ài)心加關(guān)注吧~ 后續(xù)會(huì)盡可能的分享更多高質(zhì)量的文章于大家交流學(xué)習(xí)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末驻呐,一起剝皮案震驚了整個(gè)濱河市灌诅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌含末,老刑警劉巖猜拾,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異答渔,居然都是意外死亡关带,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)沼撕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)宋雏,“玉大人,你說(shuō)我怎么就攤上這事务豺∧プ埽” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵笼沥,是天一觀的道長(zhǎng)蚪燕。 經(jīng)常有香客問(wèn)我,道長(zhǎng)奔浅,這世上最難降的妖魔是什么馆纳? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮汹桦,結(jié)果婚禮上鲁驶,老公的妹妹穿的比我還像新娘。我一直安慰自己舞骆,他們只是感情好钥弯,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著督禽,像睡著了一般脆霎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上狈惫,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天睛蛛,我揣著相機(jī)與錄音,去河邊找鬼。 笑死玖院,一個(gè)胖子當(dāng)著我的面吹牛菠红,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播难菌,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蔑滓!你這毒婦竟也來(lái)了郊酒?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤键袱,失蹤者是張志新(化名)和其女友劉穎燎窘,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蹄咖,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡褐健,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了澜汤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蚜迅。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖俊抵,靈堂內(nèi)的尸體忽然破棺而出谁不,到底是詐尸還是另有隱情,我是刑警寧澤徽诲,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布刹帕,位于F島的核電站,受9級(jí)特大地震影響谎替,放射性物質(zhì)發(fā)生泄漏偷溺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一钱贯、第九天 我趴在偏房一處隱蔽的房頂上張望挫掏。 院中可真熱鬧,春花似錦喷舀、人聲如沸砍濒。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)爸邢。三九已至,卻和暖如春拿愧,著一層夾襖步出監(jiān)牢的瞬間杠河,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留券敌,地道東北人唾戚。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像待诅,于是被迫代替她去往敵國(guó)和親叹坦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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