一次搞定Process和Task

關(guān)于進(jìn)程-Process

影響process的屬性

控制組件運(yùn)行進(jìn)程的有兩個(gè)屬性:android:processandroid: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í)了。

  1. 前臺(tái)進(jìn)程 Activte process
    • 前臺(tái)響應(yīng)用戶事件的Activity以及與之綁定的Service
    • startForeground的Service
    • 正在執(zhí)行onStart,onCreate,OnDestroy的Service
    • 正在執(zhí)行onReceive的BroadcastReceiver
  2. 可見(jiàn)進(jìn)程 Visible Process
    • onPause但未onStop的Activity
    • 綁定到可見(jiàn)Activity的Service
  3. 服務(wù)進(jìn)程 Service process
    • 普通Service
  4. 背景進(jìn)程 Background process
    • onStop的Activity
  5. 空進(jìn)程 Empty process
    • 不包含任何組件的進(jìn)程

Process的實(shí)際使用

  1. 子進(jìn)程可以分擔(dān)主進(jìn)程的內(nèi)存壓力
  2. 在主進(jìn)程Crash時(shí),子進(jìn)程中的功能不受影響
  3. 子進(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

  1. standardsingleTop:可以包含多個(gè)Activity實(shí)例
  2. singleInstancesingleTask:只有一個(gè)Activity實(shí)例
  3. singleInstance是一個(gè)Task中唯一的Activity
  4. singleTask不一定是Task root(google文檔有問(wèn)題,但使用中建議作為Task root使用)

taskAffinity

  1. 僅有singleInstance啟動(dòng)新Task不依賴taskAffinity
  2. singleTask的Activity只有其taskAffinity和原Activity不一樣時(shí)才會(huì)啟動(dòng)新Task
  3. standardsingleTop啟動(dòng)新Task,不僅要求新的taskAffinity,而且需要FLAG_ACTIVITY_NEW_TASK
  4. 如果有后臺(tái)Task和要啟動(dòng)的Activity具有相同的taskAffinity,則不會(huì)啟動(dòng)新的Task,而是將后臺(tái)Task切換到前臺(tái),并根據(jù)其他屬性和標(biāo)簽重新安排后臺(tái)Task中的Activity和新Activity
  5. taskAffinity的值應(yīng)該是以‘.’開(kāi)頭的字符串

其他多Task屬性和標(biāo)簽

只有standardsingleTop類型的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元素

  1. 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í),不生效)例子:瀏覽器。
  2. finishOnTaskLaunch:默認(rèn)false,通過(guò)應(yīng)用圖標(biāo)重新啟動(dòng)程序時(shí),這個(gè)Activity會(huì)被銷毀。
  3. 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í)不生效)
  4. alwaysRetainTaskState:用于Task root,默認(rèn)false,在應(yīng)用切換到后臺(tái)30分鐘后會(huì)被系統(tǒng)清理。如果為true則不會(huì)被系統(tǒng)清理
  5. noHistory:頁(yè)面不可見(jiàn)時(shí)被自動(dòng)銷毀,不會(huì)保存在mHistory

其他Intent.FLAG

  1. 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)建。
  2. FLAG_ACTIVITY_REORDER_TO_FRONT:Activity會(huì)重新排序,任何Activity都不會(huì)被銷毀。
  3. FLAG_ACTIVITY_NO_HISTORY:和xml中的noHistory效果相同
  4. 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)系

Task&Process.gif
  1. ActivityManagerService通過(guò)一個(gè)列表mHistory來(lái)管理所有ActivityRecord
  2. 相同TaskRecord中的ActivityRecord在列表中處于連續(xù)位置
  3. 同一個(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ù)雜得多!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容