關(guān)于進(jìn)程-Process
影響process的屬性
控制組件運(yùn)行進(jìn)程的有兩個(gè)屬性:android:process和android:multiprocess
關(guān)于android:process
- 可分別指定Application和四大組件運(yùn)行的進(jìn)程名
- 不指定時(shí),使用包名作為進(jìn)程名
- 如果進(jìn)程名是以冒號(hào)開(kāi)頭的,則這個(gè)進(jìn)程是應(yīng)用的私有進(jìn)程
- 如果進(jìn)程名是以字符開(kāi)頭的,且符合包名規(guī)范,則這個(gè)進(jìn)程是全局的
只有provider和activity定義了android:multiprocess,但不要在activity中使用。
如果android:multiprocess為true,則每個(gè)訪問(wèn)provider的應(yīng)用都會(huì)自己創(chuàng)建一個(gè)ContentProvider實(shí)例
- 優(yōu)勢(shì):避免跨進(jìn)程通信,提高數(shù)據(jù)訪問(wèn)效率
- 弊端:多個(gè)實(shí)例導(dǎo)致系統(tǒng)內(nèi)存消耗變大,且難以處理多個(gè)進(jìn)程之間的數(shù)據(jù)同步問(wèn)題
如果需要兩個(gè)不同APK中的組件運(yùn)行于同一個(gè)進(jìn)程,需要以下三個(gè)條件:
- 兩個(gè)應(yīng)用程序使用相同的
android:sharedUserId - 兩個(gè)應(yīng)用使用相同的keystore進(jìn)行簽名
- 為組件設(shè)置相同的
android:process
不同Process帶來(lái)的影響
跨進(jìn)程通信
由于不同的進(jìn)程使用不同的內(nèi)存空間,所以不同進(jìn)程之間的通信本質(zhì)上只依賴以下四種方式
- Binder:包括Messenger、AIDL,只能傳遞基本數(shù)據(jù)類型或?qū)崿F(xiàn)了Parcelable的對(duì)象,本質(zhì)是數(shù)據(jù)的序列化和反序列化
- Intent:跨進(jìn)程的組件調(diào)用,本質(zhì)也是數(shù)據(jù)的序列化和反序列化
- ASHMEM:匿名共享內(nèi)存,需要依賴Binder傳遞共享內(nèi)存文件描述,使用系統(tǒng)簽名的APK才能直接使用
- 其他通用方式:共享文件、網(wǎng)絡(luò)接口等
ContentProvider的實(shí)現(xiàn)就是使用ASHMEM
- ContentProvider保存的數(shù)據(jù),都是以私有文件存儲(chǔ)的,其他進(jìn)程無(wú)法訪問(wèn)
- 其他程序創(chuàng)建CursorWindow時(shí),同時(shí)創(chuàng)建了一塊匿名共享內(nèi)存,并實(shí)現(xiàn)了Parcelable
- 通過(guò)Binder將CursorWindow和共享內(nèi)存文件描述傳遞給ContentProvider
- ContentProvider創(chuàng)建自己的共享內(nèi)存文件描述,并指向共享內(nèi)存
進(jìn)程優(yōu)先級(jí)
內(nèi)存不足時(shí),系統(tǒng)可能殺掉進(jìn)程,這時(shí)進(jìn)程中的Application和應(yīng)用組件也會(huì)隨之銷毀。系統(tǒng)如何選擇停止的進(jìn)程,就涉及到進(jìn)程優(yōu)先級(jí)了。
- 前臺(tái)進(jìn)程 Activte process
- 前臺(tái)響應(yīng)用戶事件的Activity以及與之綁定的Service
- startForeground的Service
- 正在執(zhí)行onStart,onCreate,OnDestroy的Service
- 正在執(zhí)行onReceive的BroadcastReceiver
- 可見(jiàn)進(jìn)程 Visible Process
- onPause但未onStop的Activity
- 綁定到可見(jiàn)Activity的Service
- 服務(wù)進(jìn)程 Service process
- 普通Service
- 背景進(jìn)程 Background process
- onStop的Activity
- 空進(jìn)程 Empty process
- 不包含任何組件的進(jìn)程
Process的實(shí)際使用
- 子進(jìn)程可以分擔(dān)主進(jìn)程的內(nèi)存壓力
- 在主進(jìn)程Crash時(shí),子進(jìn)程中的功能不受影響
- 子進(jìn)程的
Application#onCreate中可以跳過(guò)一些不必要的初始化
關(guān)于任務(wù)-Task
關(guān)于Task,有太多的參數(shù)與之相關(guān),而且他們相互影響,感覺(jué)無(wú)法窮盡。下面僅從實(shí)際的使用出發(fā),明確和Task相關(guān)的一些原則。
Task和startActivityForResult
使用startActivityForResult的必要條件是被啟動(dòng)的Activity和原Activity要在同一個(gè)Task中。因此,使用startActivityForResult時(shí),會(huì)強(qiáng)制將新啟動(dòng)的Activity放在原來(lái)的Task中,不論activiy的xml屬性和Intent#Flag_XXX。
只有一個(gè)例外FLAG_ACTIVITY_NEW_TASK:如果使用這個(gè)標(biāo)簽,原Activity會(huì)立刻收到onActivityResult,并執(zhí)行和startActivity相同的邏輯。在后面的分析中可以看到,僅憑這個(gè)FLAG是無(wú)法正真啟動(dòng)新Task的。
多Task有關(guān)的參數(shù)
可以下載多Task相關(guān)的演示代碼,修改MultiTaskActivity啟動(dòng)方法的Intent和Manifest中的屬性,就可演示這小結(jié)的大部分例子。
launchMode
-
standard和singleTop:可以包含多個(gè)Activity實(shí)例 -
singleInstance和singleTask:只有一個(gè)Activity實(shí)例 -
singleInstance是一個(gè)Task中唯一的Activity -
singleTask不一定是Task root(google文檔有問(wèn)題,但使用中建議作為Task root使用)
taskAffinity
- 僅有
singleInstance啟動(dòng)新Task不依賴taskAffinity -
singleTask的Activity只有其taskAffinity和原Activity不一樣時(shí)才會(huì)啟動(dòng)新Task -
standard和singleTop啟動(dòng)新Task,不僅要求新的taskAffinity,而且需要FLAG_ACTIVITY_NEW_TASK - 如果有后臺(tái)Task和要啟動(dòng)的Activity具有相同的
taskAffinity,則不會(huì)啟動(dòng)新的Task,而是將后臺(tái)Task切換到前臺(tái),并根據(jù)其他屬性和標(biāo)簽重新安排后臺(tái)Task中的Activity和新Activity - taskAffinity的值應(yīng)該是以‘.’開(kāi)頭的字符串
其他多Task屬性和標(biāo)簽
只有standard和singleTop類型的Activity才可能出現(xiàn)多個(gè)實(shí)例。因此,只有這兩類Activity才可能出現(xiàn)多個(gè)實(shí)例,并處于不同的Task中。下面的討論,也僅限于這兩類。
- FLAG_ACTIVITY_NEW_DOCUMENT
- 如果已經(jīng)有Activity符合Intent,則切換到該Activity所在的Task
- 否則在新的Task中啟動(dòng)新的Activity
- FLAG_ACTIVITY_MULTIPLE_TASK
- 配合FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_NEW_DOCUMENT使用,在啟動(dòng)時(shí)不再尋找匹配的Activity,而是直接啟動(dòng)新任務(wù)。
- documentLaunchMode:與前面兩個(gè)標(biāo)簽相互限制。
- always:每次啟動(dòng)時(shí)都會(huì)啟動(dòng)新的Task。等同于FLAG_ACTIVITY_NEW_DOCUMENT和FLAG_ACTIVITY_MULTIPLE_TASK
- intoExisting:如果有符合Intent的Activity存在,則不再啟動(dòng)新Task,否則啟動(dòng)新Task。等同于FLAG_ACTIVITY_NEW_DOCUMENT,且不帶FLAG_ACTIVITY_MULTIPLE_TASK
- none:默認(rèn)值。由其他標(biāo)簽和屬性決定是否啟動(dòng)新Task。
- never:使FLAG_ACTIVITY_NEW_DOCUMENT和FLAG_ACTIVITY_MULTIPLE_TASK失效。
FLAG_ACTIVITY_NEW_DOCUMENT和documentLaunchMode都是5.0的新特性。通過(guò)FLAG_ACTIVITY_NEW_DOCUMENT啟動(dòng)一個(gè)新的Task,不需要指定taskAffinity。因此,最好不要將taskAffinity與FLAG_ACTIVITY_NEW_DOCUMENT混用。
在實(shí)測(cè)中發(fā)現(xiàn),never無(wú)法使FLAG_ACTIVITY_NEW_DOCUMENT失效,只是使FLAG_ACTIVITY_MULTIPLE_TASK失效。
Task相關(guān)的其他參數(shù)
這部分的參數(shù)主要處理以下兩個(gè)問(wèn)題,演示代碼不包括這部分的例子。
- Task在最近任務(wù)中的表現(xiàn)
- 一個(gè)新Activity進(jìn)入已有的Task時(shí),如何重新安排Task中的老Activity和新Activity
其它xml元素
-
alllowTaskReparenting:默認(rèn)false,只有standard和singleTop有效,表示Activity實(shí)例可以換到其他Task中。(通過(guò)應(yīng)用圖標(biāo)啟動(dòng)程序時(shí)會(huì)生效,在程序內(nèi)通過(guò)FLAG_ACTIVITY_NEW_TASK切換任務(wù)時(shí),不生效)例子:瀏覽器。 -
finishOnTaskLaunch:默認(rèn)false,通過(guò)應(yīng)用圖標(biāo)重新啟動(dòng)程序時(shí),這個(gè)Activity會(huì)被銷毀。 -
clearTaskOnLaunch:用于Task root,默認(rèn)false,啟動(dòng)時(shí)會(huì)清空Task上的其它Activity,只保留root。(通過(guò)應(yīng)用圖標(biāo)啟動(dòng)程序時(shí)會(huì)生效,通過(guò)最近任務(wù)啟動(dòng)時(shí)不生效) -
alwaysRetainTaskState:用于Task root,默認(rèn)false,在應(yīng)用切換到后臺(tái)30分鐘后會(huì)被系統(tǒng)清理。如果為true則不會(huì)被系統(tǒng)清理 -
noHistory:頁(yè)面不可見(jiàn)時(shí)被自動(dòng)銷毀,不會(huì)保存在mHistory中
其他Intent.FLAG
- FLAG_ACTIVITY_CLEAR_TOP:?jiǎn)?dòng)的Activity存在,則不創(chuàng)建新實(shí)例,而是使用原有實(shí)例,并清空上面的其它Activity。在Activity是singleTask,或者Intent中有FLAG_ACTIVITY_SINGLE_TOP,這個(gè)Activity不會(huì)重新創(chuàng)建。
- FLAG_ACTIVITY_REORDER_TO_FRONT:Activity會(huì)重新排序,任何Activity都不會(huì)被銷毀。
- FLAG_ACTIVITY_NO_HISTORY:和xml中的
noHistory效果相同 - FLAG_ACTIVITY_TASK_ON_HOME:后退時(shí)直接回Home,而不會(huì)回到之前的Task
Task和Process的關(guān)系
之前文章的《怎么處理SaveState》的末尾也提到的Task和Process的關(guān)系。這里重復(fù)一遍。
我們先看下再ActivityManagerService中進(jìn)程Process和Task的關(guān)系

-
ActivityManagerService通過(guò)一個(gè)列表mHistory來(lái)管理所有ActivityRecord - 相同
TaskRecord中的ActivityRecord在列表中處于連續(xù)位置 - 同一個(gè)
TaskRecord中的ActivityRecord可能處于不同的ProcessRecord中
由于以下兩個(gè)因素,使得很難找到Task和進(jìn)程之間關(guān)聯(lián)的清晰線索。
- 同一Task中的Activity可能屬于不同進(jìn)程
- 進(jìn)程中不僅有Activity,還有Service和BroadcastReceiver
先看Task中Activity銷毀
- 處理的問(wèn)題:一個(gè)進(jìn)程內(nèi)部,前后臺(tái)Task的資源協(xié)調(diào)
- 觸發(fā)時(shí)機(jī):進(jìn)程使用的內(nèi)存接近上限時(shí)(根據(jù)機(jī)型不同,大約在64M~256M之間)
- 會(huì)調(diào)用Activity#onDestroy,后臺(tái)Task回到前臺(tái)時(shí)會(huì)觸發(fā)Activity#onRestoreInstanceState
再看進(jìn)程被殺
- 處理的問(wèn)題:系統(tǒng)控制中,多進(jìn)程之間的資源協(xié)調(diào)
- 觸發(fā)時(shí)機(jī):整個(gè)系統(tǒng)使用的內(nèi)存接近機(jī)器配置的內(nèi)存上限時(shí)
- 不會(huì)調(diào)用Activity#onDestroy,后臺(tái)Task回到前臺(tái)時(shí)不一定會(huì)觸發(fā)Activity#onRestoreInstanceState,和Task的啟動(dòng)方式和啟動(dòng)時(shí)間有關(guān)。
我們以一個(gè)簡(jiǎn)化的例子討論兩者的關(guān)系。假設(shè):
- 單進(jìn)程最大可使用內(nèi)存為100M,進(jìn)程使用內(nèi)存超過(guò)90M時(shí)會(huì)觸發(fā)后臺(tái)Task銷毀。
- 系統(tǒng)總可用內(nèi)存為200M,系統(tǒng)使用內(nèi)存超過(guò)190M時(shí)會(huì)觸發(fā)后臺(tái)進(jìn)程被殺。
- 系統(tǒng)中運(yùn)行著3個(gè)進(jìn)程,他們?cè)谌齻€(gè)Task中的分布和內(nèi)存使用如下
- Task1處于前臺(tái)運(yùn)行
| Momory Usage | Process1 | Process2 | Process 3 |
|---|---|---|---|
| Task1 | 60M | 20M | - |
| Task2 | 20M | 20M | - |
| Task3 | - | - | 40M |
如果T1 P1部分消耗的內(nèi)存由60M上升到75M,由于P1的總內(nèi)存消耗達(dá)到95M,所以會(huì)導(dǎo)致P1 T2中的Activity被銷毀。
如果T1 P2部分消耗的內(nèi)存由20M上升到50M,會(huì)導(dǎo)致系統(tǒng)總內(nèi)存消耗達(dá)到190M。此時(shí)三個(gè)Process中,P1和P2和前臺(tái)Task關(guān)聯(lián),優(yōu)先級(jí)較高,所以系統(tǒng)會(huì)殺掉P3。
這個(gè)例子,只是對(duì)兩者關(guān)系的一個(gè)簡(jiǎn)要說(shuō)明。系統(tǒng)對(duì)進(jìn)程的實(shí)際處理方式要復(fù)雜得多!