一鼎天、前言
Android系統(tǒng)進(jìn)程币蹋活主要包括兩個(gè)層面:
1、提高進(jìn)程的優(yōu)先級(jí)斋射,從而降低進(jìn)程被殺死的概率育勺。
2但荤、在進(jìn)程被殺死后,對(duì)進(jìn)程進(jìn)行拉活涧至。
我們先來(lái)看一下進(jìn)程的優(yōu)先級(jí):
Android系統(tǒng)將盡量保持應(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)”中赘方。在對(duì)進(jìn)程進(jìn)行終止時(shí)烧颖,首先會(huì)清除一些重要性低一點(diǎn)的進(jìn)程,以此類(lèi)推窄陡,以回收系統(tǒng)資源炕淮。
根據(jù)進(jìn)程的優(yōu)先級(jí),可以將進(jìn)行劃分為5級(jí):
1跳夭、前臺(tái)進(jìn)程(Foreground process)
2涂圆、可見(jiàn)進(jìn)程(Visible process)
3、服務(wù)進(jìn)程(Service process)
4币叹、后臺(tái)進(jìn)程(Background process)
5润歉、空進(jìn)程(Empty process)
下面分別來(lái)闡述這五種進(jìn)程的特點(diǎn):
1、前臺(tái)進(jìn)程(Foreground process)
用戶(hù)當(dāng)前操作所必需的進(jìn)程套硼。在給定時(shí)間內(nèi)卡辰,前臺(tái)進(jìn)程同時(shí)存在的數(shù)量并不多。只有在內(nèi)存不足以支持它們同時(shí)存在時(shí)邪意,系統(tǒng)才會(huì)終止它們九妈。
前臺(tái)進(jìn)程的一些例子:
A、擁有用戶(hù)正在交互的Activity(已調(diào)用onResume)
B雾鬼、擁有某個(gè)Service萌朱,該Service和用戶(hù)正在交互的Activity綁定在一起。
C策菜、擁有正在“前臺(tái)”運(yùn)行的Service(服務(wù)已調(diào)用startForeground)
D晶疼、擁有正執(zhí)行一個(gè)生命周期回調(diào)的Service(onCreate、onStartCommand或者onDestroy)
E又憨、擁有正在執(zhí)行器onReceive方法的BroadcastReceiver
2翠霍、可見(jiàn)進(jìn)程(Visible process)
沒(méi)有任何前臺(tái)組件,但仍會(huì)影響用戶(hù)在屏幕上所見(jiàn)內(nèi)容的進(jìn)程蠢莺『祝可見(jiàn)進(jìn)程被視為極其重要的進(jìn)程,除非為了維持所有前臺(tái)進(jìn)程同時(shí)運(yùn)行而必須終止可見(jiàn)進(jìn)程躏将,否則是不會(huì)終止這些進(jìn)程的锄弱。
可見(jiàn)進(jìn)程的一些例子:
A考蕾、擁有不在前臺(tái),但仍對(duì)用戶(hù)可見(jiàn)的Activity(已調(diào)用onPause)
B会宪、擁有綁定到可見(jiàn)(或者前臺(tái))Activity的Service
3肖卧、服務(wù)進(jìn)程(Service process)
服務(wù)進(jìn)程與用戶(hù)所見(jiàn)內(nèi)容沒(méi)有直接的聯(lián)系,但是它們通常在執(zhí)行一些用戶(hù)關(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)壁榕。
服務(wù)進(jìn)程的一些例子:
A、使用startService啟動(dòng)的服務(wù)赎瞎,并且不屬于上述兩個(gè)更高級(jí)別的進(jìn)程牌里。
4、后臺(tái)進(jìn)程(Background process)
后臺(tái)進(jìn)程對(duì)用戶(hù)體驗(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ì)出現(xiàn)在LRU列表中,以確保包含用戶(hù)最近查看的Activity的進(jìn)程最后一個(gè)被終止挺尿。如果某個(gè)Activity正確實(shí)現(xiàn)了生命周期方法奏黑,并保存了其當(dāng)前狀態(tài),則終止其進(jìn)程不會(huì)對(duì)用戶(hù)體驗(yàn)產(chǎn)生明顯的影響编矾,因?yàn)楫?dāng)用導(dǎo)航回該Activity時(shí)熟史,Activity會(huì)恢復(fù)所有可見(jiàn)狀態(tài)。
后臺(tái)進(jìn)程的一些例子:
A窄俏、對(duì)用戶(hù)不可見(jiàn)的Activity的進(jìn)程(已調(diào)用Activity的onStop方法)
5蹂匹、空進(jìn)程(Empty process)
保留這種進(jìn)程的唯一目的是用作緩存,以縮短下次在其中運(yùn)行組件所需的啟動(dòng)時(shí)間凹蜈。為了總體系統(tǒng)資源在進(jìn)程緩存和底層內(nèi)核緩存之間保持平衡限寞,系統(tǒng)往往會(huì)終止這些進(jìn)程。
空進(jìn)程的一些例子:
A仰坦、不含任何活動(dòng)的應(yīng)用組件的進(jìn)程履植。
二、Android進(jìn)程回收策略
Android中內(nèi)存的回收悄晃,主要是由Lowmemorykiller來(lái)完成的静尼,它是一種根據(jù)OOM_ADJ閾值級(jí)別觸發(fā)相應(yīng)力度的內(nèi)存回收機(jī)制。
一般來(lái)說(shuō)传泊,OOM_ADJ為0代表的是前臺(tái)進(jìn)程鼠渺,大于0的表示一些后臺(tái)進(jìn)程或者是服務(wù)進(jìn)程。而OOM_ADJ小于0表示非Android進(jìn)程(純Linux進(jìn)程)眷细。
在Lowmemorykiller回收內(nèi)存時(shí)會(huì)根據(jù)進(jìn)程的級(jí)別優(yōu)先殺死OOM_ADJ比較大的進(jìn)程拦盹,而對(duì)于優(yōu)先級(jí)相同的進(jìn)程則優(yōu)先殺死那些占用內(nèi)存和存活時(shí)間比較大的進(jìn)程。
怎么查看進(jìn)程的優(yōu)先級(jí)(oom_adj的值)溪椎?
1普舆、在命令行模式下,使用adb shell命令進(jìn)入shell模式校读。
2沼侣、ps命令可以列舉出所有正在運(yùn)行的進(jìn)程的信息。當(dāng)然我們可以使用ps | grep 進(jìn)程名(一般為包名)來(lái)過(guò)濾得到我們想要的進(jìn)程歉秫。
3蛾洛、從第二步獲取進(jìn)程的id,然后使用 cat /proc/進(jìn)程id/oom_adj獲取該進(jìn)程的優(yōu)先級(jí)雁芙。
具體做法可以參考下面的例子:
PS:查看進(jìn)程優(yōu)先級(jí)時(shí)轧膘,在第三步時(shí)會(huì)返回permission denied。那是因?yàn)闆](méi)有獲得手機(jī)的root權(quán)限兔甘,所以上述操作要在已root的手機(jī)或者模擬器上進(jìn)行谎碍。
綜上所述,為了減少進(jìn)程被殺的概率洞焙,則要想辦法提高進(jìn)程的優(yōu)先級(jí)蟆淀。接下來(lái)我們將從進(jìn)程優(yōu)先級(jí)的提升方面講一下幾種可行方案。
三澡匪、提升進(jìn)程優(yōu)先級(jí)的方案
1熔任、利用Activity提升進(jìn)程優(yōu)先級(jí)
1.1 方案設(shè)計(jì):
監(jiān)控手機(jī)的鎖屏和解鎖事件,在屏幕鎖屏?xí)r啟動(dòng)1個(gè)像素的Activity仙蛉,在解鎖時(shí)將Activity銷(xiāo)毀笋敞。該Activity要設(shè)計(jì)成用戶(hù)無(wú)感知。
這里有個(gè)前提是APP進(jìn)程需要在后臺(tái)荠瘪。如果APP進(jìn)程在前臺(tái)夯巷,那么進(jìn)程已經(jīng)是前臺(tái)進(jìn)程了,那么上面的做法就沒(méi)有任何意義了哀墓。
通過(guò)該方案趁餐,進(jìn)程的優(yōu)先級(jí)可以在鎖屏?xí)r由oom_adj為9(不同手機(jī)系統(tǒng)的值可能不一樣)提升到0(前臺(tái)進(jìn)程)。這樣我們的APP就不容易在鎖屏?xí)r間內(nèi)被第三方應(yīng)用給殺死篮绰。
1.2 適用范圍:
場(chǎng)景:該方案主要解決的痛點(diǎn)是第三方應(yīng)用或者系統(tǒng)管理工具會(huì)在檢測(cè)到鎖屏事件一段時(shí)間后殺死后臺(tái)進(jìn)程后雷,以達(dá)到省電的目的。
版本:適用于所有的Android版本。
1.3 具體實(shí)現(xiàn):
可參考Github上的開(kāi)源項(xiàng)目KeepProcessLive臀突。
對(duì)于該開(kāi)源項(xiàng)目勉抓,目前有一個(gè)疑問(wèn),在監(jiān)聽(tīng)手機(jī)的鎖屏解鎖事件時(shí)候学,會(huì)多次監(jiān)聽(tīng)到鎖屏藕筋、解鎖操作,導(dǎo)致有時(shí)候該方案無(wú)效梳码。
2隐圾、利用Notification提升進(jìn)程優(yōu)先級(jí)
2.1 方案設(shè)計(jì):
通過(guò)將Android中的Service的setForeground接口可以將后臺(tái)Service設(shè)置為前臺(tái)Service,這樣進(jìn)程的優(yōu)先級(jí)就被提升到了2掰茶,從而使進(jìn)程的優(yōu)先級(jí)僅僅次于用戶(hù)當(dāng)前正在交互的進(jìn)程暇藏,與可見(jiàn)進(jìn)程的優(yōu)先級(jí)一致,使進(jìn)程被殺死的概率大大降低濒蒋。
在A(yíng)ndroid 2.3開(kāi)始調(diào)用setForeground將后臺(tái)Service設(shè)置為前臺(tái)Service時(shí)盐碱,必須在系統(tǒng)的通知欄發(fā)送一條通知,這意味著前臺(tái)Service與一條可見(jiàn)的通知是綁定在一起的啊胶。但是對(duì)于不需要使用常駐通知欄的應(yīng)用來(lái)說(shuō)甸各,該方案是用戶(hù)感知的,沒(méi)有辦法直接使用焰坪。
我們可以通過(guò)實(shí)現(xiàn)一個(gè)內(nèi)部Service趣倾,在LiveService和其內(nèi)部Service中同時(shí)發(fā)送具有相同ID的Notification,然后將內(nèi)部Service結(jié)束掉某饰,同時(shí)也將Notification結(jié)束掉儒恋,隨著Notification的消失,用戶(hù)就無(wú)感知了黔漂,同時(shí)也實(shí)現(xiàn)了將系統(tǒng)優(yōu)先級(jí)提升到2的操作诫尽。
2.2 適用范圍:
版本:適用于所有的Android版本。
2.3 具體實(shí)現(xiàn):
可參考Github上的開(kāi)源項(xiàng)目KeepProcessLive炬守。
四牧嫉、進(jìn)程被殺后的拉活方案
1、利用系統(tǒng)廣播拉活
1.1 方案設(shè)計(jì):
在發(fā)生特定系統(tǒng)事件時(shí)减途,系統(tǒng)會(huì)發(fā)出響應(yīng)的廣播酣藻,如果我們?cè)贏(yíng)ndroidManifest中“靜態(tài)”注冊(cè)對(duì)應(yīng)的廣播監(jiān)聽(tīng)器,就可以在發(fā)生響應(yīng)事件時(shí)進(jìn)行拉活鳍置。
常用的用于拉活的廣播事件包括:
1.2 適用范圍:
適用于全部的Android平臺(tái)辽剧。但是存在著以下幾個(gè)缺點(diǎn):
1、廣播接收器被管理軟件或者系統(tǒng)軟件通過(guò)“自啟動(dòng)”等功能禁用的場(chǎng)景無(wú)法接收到廣播税产,從而無(wú)法自啟怕轿。
2偷崩、系統(tǒng)廣播事件是不可控制的,只能在保證發(fā)生廣播事件時(shí)拉活進(jìn)程撞羽,但是無(wú)法做到進(jìn)程掛掉后立即拉活阐斜。
因此,該方案只是作為一個(gè)備選方案放吩。
2智听、利用第三方應(yīng)用廣播拉活
2.1 方案設(shè)計(jì):
該方案的設(shè)計(jì)思想和接收系統(tǒng)廣播的類(lèi)似,不同的是該方案接收的是第三方Top應(yīng)用的廣播渡紫。
通過(guò)反編譯第三方Top應(yīng)用,如:手機(jī)QQ考赛、微信惕澎、支付寶、UC瀏覽器等颜骤,以及友盟唧喉、信鴿、個(gè)推等SDK忍抽,找出它們外發(fā)的廣播八孝,在應(yīng)用中進(jìn)行監(jiān)聽(tīng),當(dāng)這些應(yīng)用發(fā)出廣播時(shí)鸠项,就會(huì)將我們的應(yīng)用進(jìn)行拉活干跛。
2.2 適用范圍:
該方案的缺點(diǎn)和上面所說(shuō)的系統(tǒng)廣播的方案一樣之外,還有下面幾個(gè)缺點(diǎn):
1祟绊、進(jìn)程拉活的效果取決于反編譯分析過(guò)的第三方應(yīng)用的多少楼入。
2、第三方應(yīng)用的廣播屬于應(yīng)用私有的牧抽,所以有可能存在當(dāng)前版本有效的廣播在后續(xù)的版本中隨時(shí)有可能被移除或者被改為不外發(fā)嘉熊。
因此,該方案也只是作為一個(gè)備選方案扬舒。
3阐肤、利用系統(tǒng)Service機(jī)制拉活
3.1 方案設(shè)計(jì):
該方案將Service的onStartCommand方法的返回值設(shè)置為START_STICKY,利用系統(tǒng)機(jī)制在Service掛掉后自動(dòng)拉活讲坎。
3.2 適用范圍:
下面這兩種情況無(wú)法進(jìn)行拉活:
1孕惜、Service在第一次被異常殺死后會(huì)在5秒內(nèi)重啟,第二次被殺死后會(huì)在10秒重啟衣赶,第三次殺死后會(huì)在20秒重啟诊赊,但是一旦在短時(shí)間內(nèi)Service被殺死的次數(shù)達(dá)到5次,則系統(tǒng)不再拉起府瞄。
2碧磅、進(jìn)程被取得Root權(quán)限的管理工具或系統(tǒng)工具通過(guò)foreStop停止掉碘箍,則無(wú)法重啟。
4鲸郊、利用JobScheduler機(jī)制拉活
4.1 方案設(shè)計(jì):
Android 5.0以后的版本提供了JobScheduler接口丰榴,系統(tǒng)會(huì)定時(shí)調(diào)用該進(jìn)程以使應(yīng)用進(jìn)行一些邏輯操作。
4.2 適用范圍:
主要適用于A(yíng)ndroid 5.0以上的版本秆撮。在A(yíng)ndroid 5.0以上版本中不受forcestop影響四濒,被強(qiáng)制停止的應(yīng)用依然可以被拉起赎线,在A(yíng)ndroid 5.0以上版本拉活效果是非常好的你画。(但是在小米手機(jī)上可能會(huì)偶爾出現(xiàn)無(wú)法拉活的問(wèn)題。)
4.3 具體實(shí)現(xiàn):
可參考Github上的開(kāi)源項(xiàng)目KeepProcessLive预柒。
5舒裤、利用賬號(hào)同步機(jī)制拉活
5.1 方案設(shè)計(jì):
Android系統(tǒng)的賬號(hào)同步機(jī)制會(huì)定期同步賬號(hào)喳资,該方案的目的在于利用同步機(jī)制進(jìn)行進(jìn)程的拉活。
5.2 適用范圍:
適用于所有的Android版本腾供,包括forestop掉的進(jìn)程也可以進(jìn)行拉活仆邓。但是在最新Android版本(Android N)中系統(tǒng)好像對(duì)賬戶(hù)同步做了改動(dòng),所以該方法是否能夠再用還需要進(jìn)行一定的實(shí)驗(yàn)伴鳖。