作者簡(jiǎn)介 原創(chuàng)微信公眾號(hào)郭霖 WeChat ID: guolin_blog
本篇來(lái)自Gpwner的投稿,介紹了Android中進(jìn)程與線程的基礎(chǔ)知識(shí)鳄哭,希望能幫助到大家。
Gpwner的博客地址:
http://blog.csdn.net/Gpwner
前言
當(dāng)某個(gè)應(yīng)用組件啟動(dòng)且該應(yīng)用沒(méi)有運(yùn)行其他任何組件時(shí),Android 系統(tǒng)會(huì)使用單個(gè)執(zhí)行線程為應(yīng)用啟動(dòng)新的 Linux 進(jìn)程祭隔。
默認(rèn)情況下,同一應(yīng)用的所有組件在相同的進(jìn)程和線程(稱為“主”線程)中運(yùn)行路操。
如果某個(gè)應(yīng)用組件啟動(dòng)且該應(yīng)用已存在進(jìn)程(因?yàn)榇嬖谠搼?yīng)用的其他組件)疾渴,則該組件會(huì)在此進(jìn)程內(nèi)啟動(dòng)并使用相同的執(zhí)行線程千贯。 但是可以安排應(yīng)用中的其他組件在單獨(dú)的進(jìn)程中運(yùn)行,并為任何進(jìn)程創(chuàng)建額外的線程搞坝。
進(jìn)程
a).默認(rèn)情況下搔谴,同一應(yīng)用的所有組件均在相同的進(jìn)程中運(yùn)行,且大多數(shù)應(yīng)用都不會(huì)改變這一點(diǎn)桩撮。 但是敦第,如果需要控制某個(gè)組件所屬的進(jìn)程,則可在清單文件中執(zhí)行此操作店量。
各類組件元素的清單文件條目 —芜果、、 垫桂、— 均支持android:process屬性师幕,此屬性可以指定該組件應(yīng)在哪個(gè)進(jìn)程運(yùn)行。您可以設(shè)置此屬性诬滩,使每個(gè)組件均在各自的進(jìn)程中運(yùn)行霹粥,或者使一些組件共享一個(gè)進(jìn)程,而其他組件則不共享疼鸟。 此外后控,您還可以設(shè)置android:process,使不同應(yīng)用的組件在相同的進(jìn)程中運(yùn)行空镜,但前提是這些應(yīng)用共享相同的 Linux 用戶 ID 并使用相同的證書(shū)進(jìn)行簽署浩淘。
b).此外,?元素還支持android:process屬性吴攒,以設(shè)置適用于所有組件的默認(rèn)值张抄。
c).如果內(nèi)存不足,而其他為用戶提供更緊急服務(wù)的進(jìn)程又需要內(nèi)存時(shí)洼怔,Android 可能會(huì)決定在某一時(shí)刻關(guān)閉某一進(jìn)程署惯。在被終止進(jìn)程中運(yùn)行的應(yīng)用組件也會(huì)隨之銷毀。 當(dāng)這些組件需要再次運(yùn)行時(shí)镣隶,系統(tǒng)將為它們重啟進(jìn)程极谊。
決定終止哪個(gè)進(jìn)程時(shí),Android 系統(tǒng)將權(quán)衡它們對(duì)用戶的相對(duì)重要程度安岂。例如轻猖,相對(duì)于托管可見(jiàn) Activity 的進(jìn)程而言,它更有可能關(guān)閉托管屏幕上不再可見(jiàn)的 Activity 的進(jìn)程域那。 因此咙边,是否終止某個(gè)進(jìn)程的決定取決于該進(jìn)程中所運(yùn)行組件的狀態(tài)。
終止進(jìn)程所用的規(guī)則
Android 系統(tǒng)將盡量長(zhǎng)時(shí)間地保持應(yīng)用進(jìn)程,但為了新建進(jìn)程或運(yùn)行更重要的進(jìn)程败许,最終需要移除舊進(jìn)程來(lái)回收內(nèi)存友瘤。 為了確定保留或終止哪些進(jìn)程,系統(tǒng)會(huì)根據(jù)進(jìn)程中正在運(yùn)行的組件以及這些組件的狀態(tài)檐束,將每個(gè)進(jìn)程放入“重要性層次結(jié)構(gòu)”中。 必要時(shí)束倍,系統(tǒng)會(huì)首先消除重要性最低的進(jìn)程被丧,然后是重要性略遜的進(jìn)程,依此類推绪妹,以回收系統(tǒng)資源甥桂。
重要性層次結(jié)構(gòu)一共有 5 級(jí)。以下列表按照重要程度列出了各類進(jìn)程(第一個(gè)進(jìn)程最重要邮旷,將是最后一個(gè)被終止的進(jìn)程):
前臺(tái)進(jìn)程
用戶當(dāng)前操作所必需的進(jìn)程黄选。如果一個(gè)進(jìn)程滿足以下任一條件,即視為前臺(tái)進(jìn)程:
托管用戶正在交互的 Activity(已調(diào)用 Activity 的 onResume() 方法)
托管某個(gè) Service婶肩,后者綁定到用戶正在交互的 Activity
托管正在“前臺(tái)”運(yùn)行的 Service(服務(wù)已調(diào)用 startForeground())
托管正執(zhí)行一個(gè)生命周期回調(diào)的 Service(onCreate()办陷、onStart() 或 onDestroy())
托管正執(zhí)行其 onReceive() 方法的 BroadcastReceiver
通常,在任意給定時(shí)間前臺(tái)進(jìn)程都為數(shù)不多律歼。只有在內(nèi)存不足以支持它們同時(shí)繼續(xù)運(yùn)行這一萬(wàn)不得已的情況下民镜,系統(tǒng)才會(huì)終止它們。 此時(shí)险毁,設(shè)備往往已達(dá)到內(nèi)存分頁(yè)狀態(tài)制圈,因此需要終止一些前臺(tái)進(jìn)程來(lái)確保用戶界面正常響應(yīng)
可見(jiàn)進(jìn)程
沒(méi)有任何前臺(tái)組件、但仍會(huì)影響用戶在屏幕上所見(jiàn)內(nèi)容的進(jìn)程畔况。 如果一個(gè)進(jìn)程滿足以下任一條件鲸鹦,即視為可見(jiàn)進(jìn)程:
托管不在前臺(tái)兼砖、但仍對(duì)用戶可見(jiàn)的 Activity(已調(diào)用其 onPause() 方法)塑悼。例如,如果前臺(tái) Activity 啟動(dòng)了一個(gè)對(duì)話框息尺,允許在其后顯示上一 Activity域庇,則有可能會(huì)發(fā)生這種情況嵌戈。
托管綁定到可見(jiàn)(或前臺(tái))Activity 的 Service。
可見(jiàn)進(jìn)程被視為是極其重要的進(jìn)程听皿,除非為了維持所有前臺(tái)進(jìn)程同時(shí)運(yùn)行而必須終止熟呛,否則系統(tǒng)不會(huì)終止這些進(jìn)程。
服務(wù)進(jìn)程
正在運(yùn)行已使用 startService() 方法啟動(dòng)的服務(wù)且不屬于上述兩個(gè)更高類別進(jìn)程的進(jìn)程尉姨。盡管服務(wù)進(jìn)程與用戶所見(jiàn)內(nèi)容沒(méi)有直接關(guān)聯(lián)庵朝,但是它們通常在執(zhí)行一些用戶關(guān)心的操作(例如,在后臺(tái)播放音樂(lè)或從網(wǎng)絡(luò)下載數(shù)據(jù))。因此九府,除非內(nèi)存不足以維持所有前臺(tái)進(jìn)程和可見(jiàn)進(jìn)程同時(shí)運(yùn)行椎瘟,否則系統(tǒng)會(huì)讓服務(wù)進(jìn)程保持運(yùn)行狀態(tài)。
后臺(tái)進(jìn)程
包含目前對(duì)用戶不可見(jiàn)的 Activity 的進(jìn)程(已調(diào)用 Activity 的 onStop() 方法)侄旬。這些進(jìn)程對(duì)用戶體驗(yàn)沒(méi)有直接影響肺蔚,系統(tǒng)可能隨時(shí)終止它們,以回收內(nèi)存供前臺(tái)進(jìn)程儡羔、可見(jiàn)進(jìn)程或服務(wù)進(jìn)程使用宣羊。 通常會(huì)有很多后臺(tái)進(jìn)程在運(yùn)行,因此它們會(huì)保存在 LRU (最近最少使用)列表中汰蜘,以確保包含用戶最近查看的 Activity 的進(jìn)程最后一個(gè)被終止仇冯。如果某個(gè) Activity 正確實(shí)現(xiàn)了生命周期方法,并保存了其當(dāng)前狀態(tài)族操,則終止其進(jìn)程不會(huì)對(duì)用戶體驗(yàn)產(chǎn)生明顯影響苛坚,因?yàn)楫?dāng)用戶導(dǎo)航回該 Activity 時(shí),Activity 會(huì)恢復(fù)其所有可見(jiàn)狀態(tài)色难。
空進(jìn)程
不含任何活動(dòng)應(yīng)用組件的進(jìn)程泼舱。保留這種進(jìn)程的的唯一目的是用作緩存,以縮短下次在其中運(yùn)行組件所需的啟動(dòng)時(shí)間莱预。 為使總體系統(tǒng)資源在進(jìn)程緩存和底層內(nèi)核緩存之間保持平衡柠掂,系統(tǒng)往往會(huì)終止這些進(jìn)程。
根據(jù)進(jìn)程中當(dāng)前活動(dòng)組件的重要程度依沮,Android 會(huì)將進(jìn)程評(píng)定為它可能達(dá)到的最高級(jí)別涯贞。例如,如果某進(jìn)程托管著服務(wù)和可見(jiàn) Activity危喉,則會(huì)將此進(jìn)程評(píng)定為可見(jiàn)進(jìn)程宋渔,而不是服務(wù)進(jìn)程。
此外辜限,一個(gè)進(jìn)程的級(jí)別可能會(huì)因其他進(jìn)程對(duì)它的依賴而有所提高皇拣,即服務(wù)于另一進(jìn)程的進(jìn)程其級(jí)別永遠(yuǎn)不會(huì)低于其所服務(wù)的進(jìn)程。 例如薄嫡,如果進(jìn)程 A 中的內(nèi)容提供程序?yàn)檫M(jìn)程 B 中的客戶端提供服務(wù)氧急,或者如果進(jìn)程 A 中的服務(wù)綁定到進(jìn)程 B 中的組件,則進(jìn)程 A 始終被視為至少與進(jìn)程 B 同樣重要毫深。
由于運(yùn)行服務(wù)的進(jìn)程其級(jí)別高于托管后臺(tái) Activity 的進(jìn)程吩坝,因此啟動(dòng)長(zhǎng)時(shí)間運(yùn)行操作的 Activity 最好為該操作啟動(dòng)服務(wù),而不是簡(jiǎn)單地創(chuàng)建工作線程哑蔫,當(dāng)操作有可能比 Activity 更加持久時(shí)尤要如此钉寝。例如弧呐,正在將圖片上傳到網(wǎng)站的 Activity 應(yīng)該啟動(dòng)服務(wù)來(lái)執(zhí)行上傳,這樣一來(lái)嵌纲,即使用戶退出 Activity俘枫,仍可在后臺(tái)繼續(xù)執(zhí)行上傳操作。使用服務(wù)可以保證逮走,無(wú)論 Activity 發(fā)生什么情況鸠蚪,該操作至少具備“服務(wù)進(jìn)程”優(yōu)先級(jí)。 同理师溅,廣播接收器也應(yīng)使用服務(wù)邓嘹,而不是簡(jiǎn)單地將耗時(shí)冗長(zhǎng)的操作放入線程中。
線程
應(yīng)用啟動(dòng)時(shí)险胰,系統(tǒng)會(huì)為應(yīng)用創(chuàng)建一個(gè)名為“主線程”的執(zhí)行線程。 此線程非常重要矿筝,因?yàn)樗?fù)責(zé)將事件分派給相應(yīng)的用戶界面小部件起便,其中包括繪圖事件。 此外窖维,它也是應(yīng)用與 Android UI 工具包組件(來(lái)自 android.widget 和 android.view 軟件包的組件)進(jìn)行交互的線程榆综。因此,主線程有時(shí)也稱為 UI 線程铸史。
系統(tǒng)不會(huì)為每個(gè)組件實(shí)例創(chuàng)建單獨(dú)的線程鼻疮。運(yùn)行于同一進(jìn)程的所有組件均在 UI 線程中實(shí)例化,并且對(duì)每個(gè)組件的系統(tǒng)調(diào)用均由該線程進(jìn)行分派琳轿。 因此判沟,響應(yīng)系統(tǒng)回調(diào)的方法(例如,報(bào)告用戶操作的 onKeyDown() 或生命周期回調(diào)方法)始終在進(jìn)程的 UI 線程中運(yùn)行崭篡。
例如挪哄,當(dāng)用戶觸摸屏幕上的按鈕時(shí),應(yīng)用的 UI 線程會(huì)將觸摸事件分派給小部件琉闪,而小部件反過(guò)來(lái)又設(shè)置其按下?tīng)顟B(tài)迹炼,并將失效請(qǐng)求發(fā)布到事件隊(duì)列中。 UI 線程從隊(duì)列中取消該請(qǐng)求并通知小部件應(yīng)該重繪自身颠毙。
在應(yīng)用執(zhí)行繁重的任務(wù)以響應(yīng)用戶交互時(shí)斯入,除非正確實(shí)現(xiàn)應(yīng)用,否則這種單線程模式可能會(huì)導(dǎo)致性能低下蛀蜜。 具體地講刻两,如果 UI 線程需要處理所有任務(wù),則執(zhí)行耗時(shí)很長(zhǎng)的操作(例如涵防,網(wǎng)絡(luò)訪問(wèn)或數(shù)據(jù)庫(kù)查詢)將會(huì)阻塞整個(gè) UI闹伪。 一旦線程被阻塞沪铭,將無(wú)法分派任何事件,包括繪圖事件偏瓤。 從用戶的角度來(lái)看杀怠,應(yīng)用顯示為掛起。 更糟糕的是厅克,如果 UI 線程被阻塞超過(guò)幾秒鐘時(shí)間(目前大約是 5 秒鐘)赔退,用戶就會(huì)看到一個(gè)讓人厭煩的“應(yīng)用無(wú)響應(yīng)”(ANR) 對(duì)話框。如果引起用戶不滿证舟,他們可能就會(huì)決定退出并卸載此應(yīng)用硕旗。
Android UI 工具包并非線程安全工具包。因此女责,您不得通過(guò)工作線程操縱 UI漆枚,而只能通過(guò) UI 線程操縱用戶界面。 因此抵知,Android 的單線程模式必須遵守兩條規(guī)則:
i. 不要阻塞 UI 線程;
ii. 不要在 UI 線程之外訪問(wèn) Android UI 工具包工作線程;
工作線程
根據(jù)上述單線程模式墙基,要保證應(yīng)用 UI 的響應(yīng)能力,關(guān)鍵是不能阻塞 UI 線程刷喜。 如果執(zhí)行的操作不能很快完成残制,則應(yīng)確保它們?cè)趩为?dú)的線程(“后臺(tái)”或“工作”線程)中運(yùn)行。
以下代碼演示了一個(gè)點(diǎn)擊監(jiān)聽(tīng)器從單獨(dú)的線程下載圖像并將其顯示在 ImageView 中:
乍看起來(lái)掖疮,這段代碼似乎運(yùn)行良好初茶,因?yàn)樗鼊?chuàng)建了一個(gè)新線程來(lái)處理網(wǎng)絡(luò)操作。 但是浊闪,它違反了單線程模式的第二條規(guī)則:不要在 UI 線程之外訪問(wèn) Android UI 工具包 — 此示例從工作線程(而不是 UI 線程)修改了 ImageView恼布。 這可能導(dǎo)致出現(xiàn)不明確、不可預(yù)見(jiàn)的行為搁宾,但要跟蹤此行為困難而又費(fèi)時(shí)桥氏。
為解決此問(wèn)題,Android 提供了幾種途徑來(lái)從其他線程訪問(wèn) UI 線程猛铅。 以下列出了幾種有用的方法:
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
可以通過(guò)使用 View.post(Runnable) 方法修復(fù)上述代碼:
上述實(shí)現(xiàn)屬于線程安全型:在單獨(dú)的線程中完成網(wǎng)絡(luò)操作字支,而在 UI 線程中操縱?ImageView。
但是奸忽,隨著操作日趨復(fù)雜堕伪,這類代碼也會(huì)變得復(fù)雜且難以維護(hù)。 要通過(guò)工作線程處理更復(fù)雜的交互栗菜,可以考慮在工作線程中使用 Handler 處理來(lái)自 UI 線程的消息欠雌。當(dāng)然,最好的解決方案或許是擴(kuò)展 AsyncTask 類疙筹,此類簡(jiǎn)化了與 UI 進(jìn)行交互所需執(zhí)行的工作線程任務(wù)富俄。
AsyncTask 允許對(duì)用戶界面執(zhí)行異步操作禁炒。 它會(huì)先阻塞工作線程中的操作,然后在 UI 線程中發(fā)布結(jié)果霍比,而無(wú)需您親自處理線程和/或處理程序幕袱。
要使用它,必須創(chuàng)建 AsyncTask 的子類并實(shí)現(xiàn) doInBackground() 回調(diào)方法悠瞬,該方法將在后臺(tái)線程池中運(yùn)行们豌。 要更新 UI,應(yīng)該實(shí)現(xiàn) onPostExecute() 以傳遞 doInBackground() 返回的結(jié)果并在 UI 線程中運(yùn)行浅妆,以便安全地更新 UI望迎。 可以通過(guò)從 UI 線程調(diào)用 execute() 來(lái)運(yùn)行任務(wù)。
例如凌外,可以通過(guò)以下方式使用 AsyncTask 來(lái)實(shí)現(xiàn)上述示例:
現(xiàn)在 UI 是安全的辩尊,代碼也得到簡(jiǎn)化,因?yàn)槿蝿?wù)分解成了兩部分:一部分應(yīng)在工作線程內(nèi)完成康辑,另一部分應(yīng)在 UI 線程內(nèi)完成对省。
AsyncTask 使用概述:
可以使用泛型指定參數(shù)類型、進(jìn)度值和任務(wù)最終值晾捏;
方法 doInBackground() 會(huì)在工作線程上自動(dòng)執(zhí)行;
onPreExecute()哀托、onPostExecute() 和 onProgressUpdate() 均在 UI 線程中調(diào)用惦辛;
doInBackground() 返回的值將發(fā)送到 onPostExecute();
可以隨時(shí)在 doInBackground() 中調(diào)用publishProgress()仓手,以在 UI 線程中執(zhí)行 onProgressUpdate()
注意:使用工作線程時(shí)可能會(huì)遇到另一個(gè)問(wèn)題胖齐,即:運(yùn)行時(shí)配置變更(例如,用戶更改了屏幕方向)導(dǎo)致 Activity 意外重啟嗽冒,這可能會(huì)銷毀工作線程呀伙。 要了解如何在這種重啟情況下堅(jiān)持執(zhí)行任務(wù),以及如何在 Activity 被銷毀時(shí)正確地取消任務(wù)
完添坊。剿另。。贬蛙。雨女。。阳准。氛堕。。野蝇。讼稚。括儒。。锐想。帮寻。。痛倚。规婆。。蝉稳。抒蚜。
文章原創(chuàng)作者GuoLin 書(shū)籍推薦
郭林大神原創(chuàng)android 書(shū)籍:《第一行代碼 android》