今天的話題比較敏感,關于 進程如何?;?。
一方面非常之多的 應用有這方面的需求并實際應用,另一方面很多應用在?;钸@條道上一路走到黑,罔顧對能耗與用戶體驗的影響,也是造成 平臺被用戶詬病的原因之一,因此一開始收到這篇投稿,是否推送給大家也是有猶豫。
我其實比較能理解保活背后的動因,只需一些簡單的代碼,就能給產品關鍵指標帶來非常大的提升,這筆賬不難算,無怪乎幾乎所有大廠都在應用各種保活方案。
另外一個比較現實的問題是 原生的推送方案因為眾所周知的原因在國內無法普及,也導致各種三方推送服務不得不想方設法謀求永生,很多IM應用也有這方面的強需求,相信大部分人都不希望錯過微信消息提醒。
我個人以為,一味去譴責所有做保活方案的同行們,未免過于苛責,至少應該先涉法了解其中的技術細節,各種方案的優劣,對系統、對用戶的影響,盡可能在現實需要與用戶體驗之間謀求平衡ps 查看進程啟動時間,盡量說服對技術不了解的同事們。比如絕大多數應用我不認為有必要?;睿撋撍浪?,交給系統決定就好。
今天 Clock 同學的這篇文章,就來自于他的學習總結,比較全面地介紹各種進程?;罘桨福缜拔⑿诺耐瑢W也發過一篇?;钗恼?,其中提到過拆分進程的?;罘桨福蚺c業務結合較為緊密,本文并無介紹。有興趣的同學也可自行搜索了解。
Clock 同學的文章傳閱度都很高,是我認識的比較善于寫文章的同學之一,有興趣的同學也可以看看他之前的投稿文章:
早前,我在知乎上回答了這樣一個問題:怎么讓 程序一直后臺運行,像 一樣不被殺死?
關于 平臺的進程?;钸@一塊,想必是所有 開發者矚目的內容之一。你到網上搜 進程?;?,可以搜出各種各樣神乎其技的做法,絕大多數都是極其不靠譜。前段時間,還出現了一個很火的“黑科技”進程保活庫,聲稱可以做到進程永生不死。
懷著學習和膜拜的心情進去圍觀,結果發現很多人提了 Issue 說各種各樣的機子無法成功保活。
看到這里,我瞬間就放心了。坦白的講,我是真心不希望有這種黑科技存在的,它只會滋生更多的流氓應用,拖垮我大 平臺的流暢性。
扯了這么多,接下來就直接進入本文的正題,談談關于進程保活的知識。提前聲明以下四點
?;钍侄?/p>
當前業界的進程保活手段主要分為 黑、白、灰 三種,其大致的實現思路如下:
黑色保活:不同的app進程,用廣播相互喚醒(包括利用系統提供的廣播進行喚醒)
白色?;睿簡忧芭_
灰色?;睿豪孟到y的漏洞啟動前臺
黑色保活
所謂黑色?;?,就是利用不同的app進程使用廣播來進行相互喚醒。舉個3個比較常見的場景:
場景1:開機,網絡切換、拍照、拍視頻時候,利用系統產生的廣播喚醒app
場景2:接入第三方SDK也會喚醒相應的app進程,如微信sdk會喚醒微信,支付寶sdk會喚醒支付寶。由此發散開去,就會直接觸發了下面的 場景3
場景3:假如你手機里裝了支付寶、淘寶、天貓、UC等阿里系的app,那么你打開任意一個阿里系的app后,有可能就順便把其他阿里系的app給喚醒了。(只是拿阿里打個比方,其實BAT系都差不多)
沒錯,我們的手機就是一步一步的被上面這些場景給拖卡機的。
針對場景1,估計已經開始意識到這些問題,所以在最新的 N取消了 (拍照),(拍視頻),(網絡切換)等三種廣播,無疑給了很多app沉重的打擊。我猜他們的心情是下面這樣的
而開機廣播的話,記得有一些定制ROM的廠商早已經將其去掉。
針對場景2和場景3,因為調用SDK喚醒app進程屬于正常行為,此處不討論。但是在借助LBE分析app之間的喚醒路徑的時候,發現了兩個問題:
很多推送SDK也存在喚醒app的功能
app之間的喚醒路徑真是多,且錯綜復雜
我把自己使用的手機測試結果給大家圍觀一下(我的手機是小米4C,刷了原生的.1系統,且已經獲得Root權限才能查看這些喚醒路徑)
15組相互喚醒路徑
全部喚醒路徑
我們直接點開 簡書 的喚醒路徑進行查看
可以看到以上3條喚醒路徑,但是涵蓋的喚醒應用總數卻達到了23+43+28款,數目真心驚人。請注意,這只是我手機上一款app的喚醒路徑而已,到了這里是不是有點細思極恐。
當然,這里依然存在一個疑問,就是LBE分析這些喚醒路徑和互相喚醒的應用是基于什么思路,我們不得而知。所以我們也無法確定其分析結果是否準確,如果有LBE的童鞋看到此文章,不知可否告知一下思路呢?但是,手機打開一個app就喚醒一大批,我自己可是親身體驗到這種酸爽的……
白色?;?/p>
白色?;钍侄畏浅:唵危褪钦{用系統api啟動一個前臺的進程,這樣會在系統的通知欄生成一個,用來讓用戶知道有這樣一個app在運行著,哪怕當前的app退到了后臺。
如下方的LBE和QQ音樂這樣:
灰色?;?/p>
灰色?;?,這種?;钍侄问菓梅秶顝V泛。它是利用系統的漏洞來啟動一個前臺的進程,與普通的啟動方式區別在于,它不會在系統通知欄處出現一個,看起來就如同運行著一個后臺進程一樣。這樣做帶來的好處就是,用戶無法察覺到你運行著一個前臺進程(因為看不到),但你的進程優先級又是高于普通后臺進程的。那么如何利用系統的漏洞呢,大致的實現思路和代碼如下:
public class GrayService extends Service {
? ?private final static int GRAY_SERVICE_ID = 1001;
? ?@Override
? ?public int onStartCommand(Intent intent, int flags, int startId) {
? ? ? ?if (Build.VERSION.SDK_INT < 18) {
? ? ? ? ? ?//API < 18 ,此方法能有效隱藏Notification上的圖標
? ? ? ? ? ?startForeground(GRAY_SERVICE_ID, new Notification());
? ? ? ?} else {
? ? ? ? ? ?Intent innerIntent = new Intent(this, GrayInnerService.class);
? ? ? ? ? ?startService(innerIntent);
? ? ? ? ? ?startForeground(GRAY_SERVICE_ID, new Notification());
? ? ? ?}
? ? ? ?
? ? ? ?return super.onStartCommand(intent, flags, startId);
? ?}
? ?
? ?...
? ?...
? ?
? ?/**
? ? * 給 API >= 18 的平臺上用的灰色?;钍侄?br /> ? ? */
? ?public static class GrayInnerService extends Service {
? ?
? ? ? ?@Override
? ? ? ?public int onStartCommand(Intent intent, int flags, int startId) {
? ?
? ? ? ? ? ?startForeground(GRAY_SERVICE_ID, new Notification());
? ? ? ? ? ?stopForeground(true);
? ? ? ? ? ?stopSelf();
? ? ? ? ? ?return super.onStartCommand(intent, flags, startId);
? ? ? ?}
? ?}
}
代碼大致就是這樣,能讓你神不知鬼不覺的啟動著一個前臺。其實市面上很多app都用著這種灰色保活的手段,什么?你不信?好吧,我們來驗證一下。流程很簡單,打開一個app,看下系統通知欄有沒有一個 ,如果沒有ps 查看進程啟動時間,我們就進入手機的adb shell模式,然后輸入下面的shell命令
dumpsys activity services PackageName
打印出指定包名的所有進程中的信息,看下有沒有 =true 的關鍵信息。如果通知欄沒有看到屬于app的 且又看到 =true 則說明了,此app利用了這種灰色保活的手段。
下面分別是我手機上微信、qq、支付寶、陌陌的測試結果,大家有興趣也可以自己驗證一下。
微信
手Q
支付寶
陌陌
其實察覺到了此漏洞的存在,并逐步進行封堵。這就是為什么這種?;罘绞椒?API >= 18 和 API < 18 兩種情況,從.0的類的函數源代碼中可以看到這樣的一行注釋
當某一天 API >= 18 的方案也失效的時候,我們就又要另謀出路了。需要注意的是,使用灰色保活并不代表著你的就永生不死了,只能說是提高了進程的優先級。如果你的app進程占用了大量的內存,按照回收進程的策略,同樣會干掉你的app。感興趣于灰色?;钍侨绾卫孟到y漏洞不顯示 的童鞋,可以研究一下系統的 、 等相關源代碼,因為不是本文的重點,所以不做詳述。
嘮叨的分割線
到這里基本就介紹完了 黑、白、灰 三種實現方式,僅僅從代碼層面去講保活是不夠的,我希望能夠通過系統的進程回收機制來理解?;?,這樣能夠讓我們更好的避免踩到進程被殺的坑。
進程回收機制
熟悉系統的童鞋都知道,系統出于體驗和性能上的考慮,app在退到后臺時系統并不會真正的kill掉這個進程,而是將其緩存起來。打開的應用越多,后臺緩存的進程也越多。在系統內存不足的情況下,系統開始依據自身的一套進程回收機制來判斷要kill掉哪些進程,以騰出內存來供給需要的app。這套殺進程回收內存的機制就叫 Low ,它是基于Linux內核的 OOM (Out-Of- )機制誕生。
了解完 Low ,再科普一下。什么是?它是linux內核分配給每個系統進程的一個值,代表進程的優先級,進程回收機制就是根據這個優先級來決定是否進行回收。對于的作用,你只需要記住以下幾點即可:
那么我們如何查看進程的值呢,需要用到下面的兩個shell命令
ps | grep PackageName //獲取你指定的進程信息
這里是以我寫的demo代碼為例子,紅色圈中部分別為下面三個進程的ID
UI進程:com.clock.
普通后臺進程:com.clock.:bg
灰色?;钸M程:com.clock.:gray
當然,這些進程的id也可以通過獲得
接著我們來再來獲取三個進程的
cat /proc/進程ID/oom_adj
從上圖可以看到UI進程和灰色保活進程的=0,而普通后臺進程=15。到這里估計你也能明白,為什么普通的后臺進程容易被回收,而前臺進程則不容易被回收了吧。
但明白這個還不夠,接著看下圖
上面是我把app切換到后臺,再進行一次的檢驗,你會發現UI進程的值從0變成了6,而灰色?;畹倪M程則從0變成了1。這里可以觀察到,app退到后臺時,其所有的進程優先級都會降低。但是UI進程是降低最為明顯的,因為它占用的內存資源最多,系統內存不足的時候肯定優先殺這些占用內存高的進程來騰出資源。所以,為了盡量避免后臺UI進程被殺,需要盡可能的釋放一些不用的資源,尤其是圖片、音視頻之類的。
從官方文檔中,我們也能看到優先級從高到低列出了這些不同類型的進程: 、 、 、 、Empty 。而這些進程的分別是多少,又是如何掛鉤起來的呢?推薦大家閱讀下面這篇文章:
總結(文末有福利)
絮絮叨叨寫完了這么多,最后來做個小小的總結。回歸到開篇提到QQ進程不死的問題,我也曾認為存在這樣一種技術。可惜我把手機root后,殺掉QQ進程之后就再也起不來了。有些手機廠商把這些知名的app放入了自己的白名單中,保證了進程不死來提高用戶體驗(如微信、QQ、陌陌都在小米的白名單中)。如果從白名單中移除,他們終究還是和普通app一樣躲避不了被殺的命運,為了盡量避免被殺,還是老老實實去做好優化工作吧。
所以,進程保活的根本方案終究還是回到了性能優化上,進程永生不死終究是個徹頭徹尾的偽命題!
文章到此結束,相關簡單的實踐代碼請看
為了感謝看完本文的童鞋,特地獻上福利圖片一張。。。。。。。。。請注意:
如果你屏幕旁有人在,請謹慎往下觀看?。。。。。。。?!
如果你屏幕旁有人在,請謹慎往下觀看!!?。。。。。?!
如果你屏幕旁有人在,請謹慎往下觀看!?。。。。。。?!
福利