在Android中述呐,默認情況下吗坚,同一應用的所有組件均運行在同一進程中菇晃,且大多數(shù)應用都不會改變這一點册倒。不過,單進程開發(fā)并不是Android應用的全部磺送,今天我們就來說說Android中的多進程開發(fā)以及多進程的使用場景剩失。
學習Android的同學注意了!2嶙拧拴孤!
學習過程中遇到什么問題或者想獲取學習資源的話,歡迎加入Android學習交流群甲捏,群號碼:364595326? 我們一起學Android演熟!
本篇文章內(nèi)容基于Android Developer官網(wǎng)
我們都知道Android系統(tǒng)是基于Linux改造而來的,進程系統(tǒng)也是一脈相承司顿,進程芒粹,其實就是程序的具體實現(xiàn)。當程序第一次啟動大溜,Android會啟動一個Linux進程(具體由Zygote fork出來)以及一個主線程化漆,默認的情況下,所有組件都將運行在該進程內(nèi)钦奋。同一個應用由系統(tǒng)分配一個獨立的Linux賬戶座云,該應用的產(chǎn)生的所有進程疙赠,都會是這同一個Linux賬戶。
在開發(fā)中朦拖,我們通常會使用修改清單文件的android:process來達到多進程的目的圃阳。activity、service璧帝、receiver和provider均支持android:process屬性捍岳,此屬性可以指定該組件應在哪個進程運行。如果android:process的value值以冒號開頭的話睬隶,那么該進程就是私有進程锣夹,如果是以其他字符開頭,那么就是公有進程苏潜,擁有相同ShareUID的不同應用可以跑在同一進程里晕城,后續(xù)我會專門針對公私有進程做個試驗。另外窖贤,我們還可以通過設置application的android:process屬性砖顷,來設置所有組件的默認進程。
至于創(chuàng)建進程的具體源碼分析赃梧,網(wǎng)上有一篇很詳細的文章滤蝠,在這就不重復造輪子了,有需要的朋友可以前往理解Android進程創(chuàng)建流程
還有一種方法開啟進程授嘀,是通過JNI物咳,利用C/C++,調(diào)用fork()方法來生成子進程蹄皱,一般開發(fā)者會利用這種方法來做一些daemon進程览闰,來實現(xiàn)防殺,毕镎郏活等效果压鉴,不過不是太推薦,這么做锻拘,畢竟Android生態(tài)系統(tǒng)需要大家維護油吭。
剛剛聊了一下進程的“生”,作為一個生命周期署拟,是時候該聊聊進程的“死”了婉宰。這里再次呼吁一下大家能正視進程的“死”,合理的利用多進程推穷,適當?shù)臍⑺啦槐匾倪M程才是本篇文章所關注的焦點心包,我們不要把“永生”作為自己的實現(xiàn)目標,Android設備內(nèi)存就那么大馒铃,就像地球一樣蟹腾,大家都永生了痕惋,生態(tài)系統(tǒng)就會破壞。那么Android系統(tǒng)是如何維護這個生態(tài)系統(tǒng)的呢岭佳?
其實也是類似于現(xiàn)實生活中的優(yōu)勝略汰,Android利用重要性層次結(jié)構萧锉,就是將最重要的保留珊随,殺掉不重要的進程。Android將重要性層次結(jié)構分為5個層級柿隙,分為了:(以下5級描述節(jié)選自Android進程生命周期)
前臺進程
用戶當前操作所必需的進程叶洞。如果一個進程滿足以下任一條件,即視為前臺進程:
托管用戶正在交互的Activity(已調(diào)用Activity的onResume()方法)
托管某個Service禀崖,后者綁定到用戶正在交互的Activity
托管正在“前臺”運行的Service(服務已調(diào)用startForeground())
托管正執(zhí)行一個生命周期回調(diào)的Service(onCreate()衩辟、onStart()或onDestroy())
托管正執(zhí)行其onReceive()方法的BroadcastReceiver
通常,在任意給定時間前臺進程都為數(shù)不多波附。只有在內(nèi)存不足以支持它們同時繼續(xù)運行這一萬不得已的情況下艺晴,系統(tǒng)才會終止它們。 此時掸屡,設備往往已達到內(nèi)存分頁狀態(tài)封寞,因此需要終止一些前臺進程來確保用戶界面正常響應。
這就需要依靠系統(tǒng)的資源仅财。
可見進程
沒有任何前臺組件狈究、但仍會影響用戶在屏幕上所見內(nèi)容的進程。 如果一個進程滿足以下任一條件盏求,即視為可見進程:
托管不在前臺抖锥、但仍對用戶可見的Activity(已調(diào)用其onPause()方法)。例如碎罚,如果前臺Activity啟動了一個對話框磅废,允許在其后顯示上一Activity,則有可能會發(fā)生這種情況荆烈。
托管綁定到可見(或前臺)Activity的Service还蹲。
可見進程被視為是極其重要的進程,除非為了維持所有前臺進程同時運行而必須終止耙考,否則系統(tǒng)不會終止這些進程谜喊。
服務進程
正在運行已使用startService()方法啟動的服務且不屬于上述兩個更高類別進程的進程。盡管服務進程與用戶所見內(nèi)容沒有直接關聯(lián)倦始,但是它們通常在執(zhí)行一些用戶關心的操作(例如斗遏,在后臺播放音樂或從網(wǎng)絡下載數(shù)據(jù))。因此鞋邑,除非內(nèi)存不足以維持所有前臺進程和可見進程同時運行诵次,否則系統(tǒng)會讓服務進程保持運行狀態(tài)账蓉。
后臺進程
包含目前對用戶不可見的Activity的進程(已調(diào)用Activity的onStop()方法)。這些進程對用戶體驗沒有直接影響逾一,系統(tǒng)可能隨時終止它們铸本,以回收內(nèi)存供前臺進程、可見進程或服務進程使用遵堵。 通常會有很多后臺進程在運行箱玷,因此它們會保存在 LRU (最近最少使用)列表中,以確保包含用戶最近查看的Activity的進程最后一個被終止陌宿。如果某個Activity正確實現(xiàn)了生命周期方法锡足,并保存了其當前狀態(tài),則終止其進程不會對用戶體驗產(chǎn)生明顯影響壳坪,因為當用戶導航回該Activity時舶得,Activity會恢復其所有可見狀態(tài)。 有關保存和恢復狀態(tài)的信息爽蝴,請參閱Activity文檔沐批。
空進程
不含任何活動應用組件的進程。保留這種進程的的唯一目的是用作緩存蝎亚,以縮短下次在其中運行組件所需的啟動時間珠插。 為使總體系統(tǒng)資源在進程緩存和底層內(nèi)核緩存之間保持平衡,系統(tǒng)往往會終止這些進程颖对。
根據(jù)進程中當前活動組件的重要程度捻撑,Android 會將進程評定為它可能達到的最高級別。例如缤底,如果某進程托管著服務和可見Activity顾患,則會將此進程評定為可見進程,而不是服務進程个唧。
此外江解,一個進程的級別可能會因其他進程對它的依賴而有所提高,即服務于另一進程的進程其級別永遠不會低于其所服務的進程徙歼。 例如犁河,如果進程 A 中的內(nèi)容提供程序為進程 B 中的客戶端提供服務,或者如果進程 A 中的服務綁定到進程 B 中的組件魄梯,則進程 A 始終被視為至少與進程 B 同樣重要桨螺。
由于運行服務的進程其級別高于托管后臺Activity的進程,因此啟動長時間運行操作的Activity最好為該操作啟動服務酿秸,而不是簡單地創(chuàng)建工作線程灭翔,當操作有可能比Activity更加持久時尤要如此。例如辣苏,正在將圖片上傳到網(wǎng)站的Activity應該啟動服務來執(zhí)行上傳肝箱,這樣一來哄褒,即使用戶退出Activity,仍可在后臺繼續(xù)執(zhí)行上傳操作煌张。使用服務可以保證呐赡,無論Activity發(fā)生什么情況,該操作至少具備“服務進程”優(yōu)先級骏融。 同理链嘀,廣播接收器也應使用服務,而不是簡單地將耗時冗長的操作放入線程中绎谦。
進程按照狀態(tài)分完重要性之后管闷,就要開始殺進程了粥脚。Android的Low Memory Killer基于Linux的OOM機制窃肠,在Linux中,內(nèi)存是以頁面(page)為單位刷允,當申請頁面分配不足的時候冤留,系統(tǒng)會通過Low Memory Killer來殺掉bad進程,釋放內(nèi)存树灶。Low Memory Killer會根據(jù)進程的adj級別以及所占的內(nèi)存纤怒,來決定是否殺掉該進程,adj越大天通,占用內(nèi)存越多泊窘,進程越容易被殺掉。
關于adj的分級像寒,我們可以參考ProcessList.java烘豹,這里面的常量定義了ADJ的分級。(7.0以后的adj分級與之前的不太一樣(Processlist.java-Nougat)诺祸,這個我們后續(xù)可以研究一下具體的改動是什么)
UNKNOWN_ADJ = 16
級別最低級的進程携悯,通常是被緩存的進程,但是系統(tǒng)也不清楚緩存的內(nèi)容筷笨。
CACHED_APP_MAX_ADJ = 15
這是一個只托管不可見的活動的進程憔鬼,因此可以在沒有任何中斷的情況下被殺死。
CACHED_APP_MIN_ADJ = 9
緩存進程胃夏,沒有英文解釋轴或。
SERVICE_B_ADJ = 8
不活躍的服務,不想adj=5的服務那么活躍仰禀。
PS:這里說一句侮叮,在root以后,有的系統(tǒng)優(yōu)化大師悼瘾,會把所有服務統(tǒng)一調(diào)成adj=8這個級別囊榜,來達到內(nèi)存優(yōu)化的目的审胸,后面我們會說到。
PREVIOUS_APP_ADJ = 7
被切換的進程卸勺,一般是用戶前一個使用的進程砂沛。兩個應用來回切換,那么前一個應用一般adj設置為7曙求。
HOME_APP_ADJ = 6
與主應用程序有交互的進程碍庵。
SERVICE_ADJ = 5
活躍的服務進程。
HEAVY_WEIGHT_APP_ADJ = 4
高權重進程
BACKUP_APP_ADJ = 3
正在備份的進程
PERCEPTIBLE_APP_ADJ = 2
可感知進程(通常是前臺Service進程)
VISIBLE_APP_ADJ = 1
可見進程
FOREGROUND_APP_ADJ = 0
前臺進程
剩下的就是adj值為負數(shù)的進程悟狱,基本上都是系統(tǒng)集成静浴,不在本文的討論范圍內(nèi)。負數(shù)進程是不會被lmk殺掉的挤渐。
首先通過 adb shell ps 指令查找對應進程的pid
然后通過 adb shell cat /proc/${pid}/oom_adj(設備需要root)返回對應進程的adj值苹享。
還可以把oom_adj替換成oom_score或者oom_score_adj來查看這兩項的數(shù)值,當oom_adj相同時浴麻,LowMemoryKiller會根據(jù)oom_score_adj和RSS內(nèi)存大小來殺掉對應的進程得问。
我們可以通過adb shell cat 查看下面兩個文件
/sys/module/lowmemorykiller/parameters/adj
/sys/module/lowmemorykiller/parameters/minfree
(這里請注意,這兩個文件是只可以寫入的软免,cat之前請先用chmod賦予權限宫纬。)
adj 代表的是oom_score_adj的值,對應的minfree則代表內(nèi)存臨界值膏萧。
比如我的測試機小米4C測試機對應的值就是:
adj: 0,58,117,176,529,1000
這個值其實是oom_score_adj的值漓骚,用這個值*17再除1000四舍五入取整數(shù),就是對應的adj的值榛泛,例如第二個值58即為 58*17/1000 = 1蝌蹂,對應的adj也就是1,所以這6個值對應的adj是0挟鸠,1叉信,2,3艘希,9硼身,15。1000默認就是15
minfree: 18432,23040,27648,32256,56250,81250
這個值是頁值覆享,一頁等于4KB佳遂,換算成MB大概是72,90撒顿,108丑罪,126,220,318
當可用內(nèi)存小于318MB的時候吩屹,系統(tǒng)開始殺adj=15的進程跪另,以此類推。