關於Android 進程保活,你所需要知道的一切

2021-01-07 開源中國

早前,我在知乎上回答了這樣一個問題:怎麼讓 Android 程序一直後臺運行,像 QQ 一樣不被殺死?。關於 Android 平臺的進程保活這一塊,想必是所有 Android 開發者矚目的內容之一。你到網上搜 Android 進程保活,可以搜出各種各樣神乎其技的做法,絕大多數都是極其不靠譜。前段時間,Github還出現了一個很火的「黑科技」進程保活庫,聲稱可以做到進程永生不死

懷著學習和膜拜的心情進去Github圍觀,結果發現很多人提了 Issue 說各種各樣的機子無法成功保活。

看到這裡,我瞬間就放心了。坦白的講,我是真心不希望有這種黑科技存在的,它只會滋生更多的流氓應用,拖垮我大 Android 平臺的流暢性。

扯了這麼多,接下來就直接進入本文的正題,談談關於進程保活的知識。提前聲明以下四點

本文是本人開發 Android 至今綜合各方資料所得

不以節能來維持進程保活的手段,都是耍流氓

本文不是教你做永生不死的進程,如果指望實現進程永生不死,請忽略本文

本文有錯誤的地方,歡迎留下評論互相探討(拍磚請輕拍)

保活手段

當前業界的Android進程保活手段主要分為 黑、白、灰 三種,其大致的實現思路如下:

黑色保活:不同的app進程,用廣播相互喚醒(包括利用系統提供的廣播進行喚醒)

白色保活:啟動前臺Service

灰色保活:利用系統的漏洞啟動前臺Service

黑色保活

所謂黑色保活,就是利用不同的app進程使用廣播來進行相互喚醒。舉個3個比較常見的場景:

場景1:開機,網絡切換、拍照、拍視頻時候,利用系統產生的廣播喚醒app

場景2:接入第三方SDK也會喚醒相應的app進程,如微信sdk會喚醒微信,支付寶sdk會喚醒支付寶。由此發散開去,就會直接觸發了下面的 場景3

場景3:假如你手機裡裝了支付寶、淘寶、天貓、UC等阿里系的app,那麼你打開任意一個阿里系的app後,有可能就順便把其他阿里系的app給喚醒了。(只是拿阿里打個比方,其實BAT系都差不多)

沒錯,我們的Android手機就是一步一步的被上面這些場景給拖卡機的。

針對場景1,估計Google已經開始意識到這些問題,所以在最新的Android N取消了 ACTION_NEW_PICTURE(拍照),ACTION_NEW_VIDEO(拍視頻),CONNECTIVITY_ACTION(網絡切換)等三種廣播,無疑給了很多app沉重的打擊。我猜他們的心情是下面這樣的

而開機廣播的話,記得有一些定製ROM的廠商早已經將其去掉。

針對場景2場景3,因為調用SDK喚醒app進程屬於正常行為,此處不討論。但是在藉助LBE分析app之間的喚醒路徑的時候,發現了兩個問題:

很多推送SDK也存在喚醒app的功能

app之間的喚醒路徑真是多,且錯綜複雜

我把自己使用的手機測試結果給大家圍觀一下(我的手機是小米4C,刷了原生的Android5.1系統,且已經獲得Root權限才能查看這些喚醒路徑

15組相互喚醒路徑

全部喚醒路徑

我們直接點開 簡書 的喚醒路徑進行查看

簡書喚醒路徑

可以看到以上3條喚醒路徑,但是涵蓋的喚醒應用總數卻達到了23+43+28款,數目真心驚人。請注意,這只是我手機上一款app的喚醒路徑而已,到了這裡是不是有點細思極恐。

當然,這裡依然存在一個疑問,就是LBE分析這些喚醒路徑和互相喚醒的應用是基於什麼思路,我們不得而知。所以我們也無法確定其分析結果是否準確,如果有LBE的童鞋看到此文章,不知可否告知一下思路呢?但是,手機打開一個app就喚醒一大批,我自己可是親身體驗到這種酸爽的......

白色保活

白色保活手段非常簡單,就是調用系統api啟動一個前臺的Service進程,這樣會在系統的通知欄生成一個Notification,用來讓用戶知道有這樣一個app在運行著,哪怕當前的app退到了後臺。如下方的LBE和QQ音樂這樣:

灰色保活

灰色保活,這種保活手段是應用範圍最廣泛。它是利用系統的漏洞來啟動一個前臺的Service進程,與普通的啟動方式區別在於,它不會在系統通知欄處出現一個Notification,看起來就如同運行著一個後臺Service進程一樣。這樣做帶來的好處就是,用戶無法察覺到你運行著一個前臺進程(因為看不到Notification),但你的進程優先級又是高於普通後臺進程的。那麼如何利用系統的漏洞呢,大致的實現思路和代碼如下:

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) {            startForeground(GRAY_SERVICE_ID, new Notification());//API < 18 ,此方法能有效隱藏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 的平臺上用的灰色保活手段     */    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);        }    }}

代碼大致就是這樣,能讓你神不知鬼不覺的啟動著一個前臺Service。其實市面上很多app都用著這種灰色保活的手段,什麼?你不信?好吧,我們來驗證一下。流程很簡單,打開一個app,看下系統通知欄有沒有一個 Notification,如果沒有,我們就進入手機的adb shell模式,然後輸入下面的shell命令

dumpsys activity services PackageName

列印出指定包名的所有進程中的Service信息,看下有沒有 isForeground=true 的關鍵信息。如果通知欄沒有看到屬於app的 Notification 且又看到 isForeground=true 則說明了,此app利用了這種灰色保活的手段。

下面分別是我手機上微信、qq、支付寶、陌陌的測試結果,大家有興趣也可以自己驗證一下。

微信

手Q

支付寶

陌陌

其實Google察覺到了此漏洞的存在,並逐步進行封堵。這就是為什麼這種保活方式分 API >= 18 和 API < 18 兩種情況,從Android5.0的ServiceRecord類的postNotification函數原始碼中可以看到這樣的一行注釋

當某一天 API >= 18 的方案也失效的時候,我們就又要另謀出路了。需要注意的是,使用灰色保活並不代表著你的Service就永生不死了,只能說是提高了進程的優先級。如果你的app進程佔用了大量的內存,按照回收進程的策略,同樣會幹掉你的app。感興趣於灰色保活是如何利用系統漏洞不顯示 Notification 的童鞋,可以研究一下系統的 ServiceRecord、NotificationManagerService 等相關原始碼,因為不是本文的重點,所以不做詳述。

嘮叨的分割線

到這裡基本就介紹完了 黑、白、灰 三種實現方式,僅僅從代碼層面去講保活是不夠的,我希望能夠通過系統的進程回收機制來理解保活,這樣能夠讓我們更好的避免踩到進程被殺的坑。

進程回收機制

熟悉Android系統的童鞋都知道,系統出於體驗和性能上的考慮,app在退到後臺時系統並不會真正的kill掉這個進程,而是將其緩存起來。打開的應用越多,後臺緩存的進程也越多。在系統內存不足的情況下,系統開始依據自身的一套進程回收機制來判斷要kill掉哪些進程,以騰出內存來供給需要的app。這套殺進程回收內存的機制就叫 Low Memory Killer ,它是基於Linux內核的 OOM Killer(Out-Of-Memory killer)機制誕生。

了解完 Low Memory Killer,再科普一下oom_adj。什麼是oom_adj?它是linux內核分配給每個系統進程的一個值,代表進程的優先級,進程回收機制就是根據這個優先級來決定是否進行回收。對於oom_adj的作用,你只需要記住以下幾點即可:

那麼我們如何查看進程的oom_adj值呢,需要用到下面的兩個shell命令

ps | grep PackageName //獲取你指定的進程信息

這裡是以我寫的demo代碼為例子,紅色圈中部分別為下面三個進程的ID

UI進程:com.clock.daemon
普通後臺進程:com.clock.daemon:bg
灰色保活進程:com.clock.daemon:gray

當然,這些進程的id也可以通過AndroidStudio獲得

接著我們來再來獲取三個進程的oom_adj

cat /proc/進程ID/oom_adj

從上圖可以看到UI進程和灰色保活Service進程的oom_adj=0,而普通後臺進程oom_adj=15。到這裡估計你也能明白,為什麼普通的後臺進程容易被回收,而前臺進程則不容易被回收了吧。但明白這個還不夠,接著看下圖

上面是我把app切換到後臺,再進行一次oom_adj的檢驗,你會發現UI進程的值從0變成了6,而灰色保活的Service進程則從0變成了1。這裡可以觀察到,app退到後臺時,其所有的進程優先級都會降低。但是UI進程是降低最為明顯的,因為它佔用的內存資源最多,系統內存不足的時候肯定優先殺這些佔用內存高的進程來騰出資源。所以,為了儘量避免後臺UI進程被殺,需要儘可能的釋放一些不用的資源,尤其是圖片、音視頻之類的

從Android官方文檔中,我們也能看到優先級從高到低列出了這些不同類型的進程:Foreground processVisible processService processBackground processEmpty process。而這些進程的oom_adj分別是多少,又是如何掛鈎起來的呢?推薦大家閱讀下面這篇文章:

http://www.cnblogs.com/angeldevil/archive/2013/05/21/3090872.html

總結(文末有福利)

絮絮叨叨寫完了這麼多,最後來做個小小的總結。回歸到開篇提到QQ進程不死的問題,我也曾認為存在這樣一種技術。可惜我把手機root後,殺掉QQ進程之後就再也起不來了。有些手機廠商把這些知名的app放入了自己的白名單中,保證了進程不死來提高用戶體驗(如微信、QQ、陌陌都在小米的白名單中)。如果從白名單中移除,他們終究還是和普通app一樣躲避不了被殺的命運,為了儘量避免被殺,還是老老實實去做好優化工作吧。

所以,進程保活的根本方案終究還是回到了性能優化上,進程永生不死終究是個徹頭徹尾的偽命題!

補充更新 (2016-04-20)

有童鞋問,在華為的機子上發現微信和手Q的UI進程退到後臺,oom_adj的值一點都沒有變,是不是有什麼黑科技在其中。為此,我稍稍驗證了一下,驗證方式就是把demo工程的包名改成手機QQ的,編譯運行在華為的機子上,發現我的進程怎麼殺也都是不死的,退到後臺oom_adj的值同樣不發生變化,而恢復原來的包名就不行了。所以,你懂的,手Q就在華為機子的白名單中。

文章到此結束,相關簡單的實踐代碼請看

https://github.com/D-clock/AndroidDaemonService

為了感謝看完本文的童鞋,特地獻上福利圖片一張......請注意:

如果你屏幕旁有人在,請謹慎往下觀看!!!!!!!!!

如果你屏幕旁有人在,請謹慎往下觀看!!!!!!!!!

如果你屏幕旁有人在,請謹慎往下觀看!!!!!!!!!

福利





文/D_clock(簡書作者)
原文連結:http://www.jianshu.com/p/63aafe3c12af


相關焦點

  • Android進程保活實踐
    進程保活的關鍵點有兩個,一個是進程優先級的理解,優先級越高存活機率越大。進程綠色部分是較難被回收的進程,屬於android進程其他部分則不是android進程,也不會被系統回收,一般是ROM自帶的app和服務才能擁有如何查看某個進程的oom_adj數值呢?
  • Android實現進程保活方案解析
    為了搶佔市場,誰都不會放過任何一個可以提高應用日活的方法,所以App進程保活都是各大廠商,特別是頭部應用開發商永恆的追求,畢竟一旦 App 進程死亡,那就再也無法在用戶的手機上開展任何業務,所有的商業模型在用戶側都沒有立足之地。
  • 探討Android6.0及以上系統APP保活實現
    對於Android6.0及其以上系統APP保活,我覺得主要還是通過這兩個方面進行,即:降低omm_adj值,儘量保證進程不被系統殺死;進程被殺死後,通過其他方式將進程復活。但需要明白的是,面對各手機廠商的深度定製和谷歌越來越嚴格的資源管理機制,這兩種方式結合的保活不是永久的,只能是相對存在,不同的機型結果也是不一樣的。
  • 史上最強Android保活思路:深入剖析騰訊TIM的進程永生技術
    這個階段的一些典型主要技術手段,可以看以下這幾篇文章:《應用保活終極總結(一):Android6.0以下的雙進程守護保活實踐》《Android進程保活詳解:一篇文章解決你的所有疑問》從strace可以推測出:com.tencent.tim:MSF進程的監控線程執行排它鎖LOCK_EX類型的flock,嘗試去獲取某個文件,而該文件已被com.tencent.tim: Daemon進程所持有,所以MSF進程會被阻塞知道鎖的釋放,而一旦Daemon進程被殺,系統就會回收所有資源(包括文件),這是Linux
  • 2018年Android的保活方案效果統計
    【公眾號回復「1024」,送你一個特別推送】一、常見保活方案
  • Android 黑科技保活實現原理揭秘
    Android 6.0 引入了待機模式(doze),一旦用戶拔下設備的電源插頭,並在屏幕關閉後的一段時間內使其保持不活動狀態,設備會進入低電耗模式,在該模式下設備會嘗試讓系統保持休眠狀態。然而,道高一尺,魔高一丈。系統在不斷演進,保活方法也在不斷發展。大約在 4 年前出現過一個 MarsDaemon,這個庫通過雙進程守護的方式實現保活,一時間風頭無兩。不過好景不長,進入 Android 8.0 時代之後,這個庫就逐漸消亡。一般來說,Android 進程保活分為兩個方面:保持進程不被系統殺死。進程被系統殺死之後,可以重新復活。
  • 另一種黑科技保活方法
    Android 黑科技保活實現原理揭秘 中的進程永生術是第二種,它通過鑽 Android 殺進程的空子實現了涅槃永生;不了解的同學可以參考一下 PoC[1]。歸根結底,所謂的黑科技就是利用系統漏洞。那麼,既然我們可以利用漏洞逃過追殺,那何不更進一步,利用系統漏洞提權?實際上,在 Android 系統中,這樣的漏洞廣泛地存在著。
  • 全面盤點當前Android後臺保活方案的真實運行效果(截止2019年前)
    其實Android端APP搞保活的目的倒不是為了幹什麼見不得人的壞事(但不排除動機不純的開發者),主要是像IM即時通訊應用和資訊類應用等需要搞後臺消息推送、運動類應用需要在後臺實時監測用戶的運動數據等,因為現在越來越多的手機廠商為了省電策略考慮,基本上如果你的應用沒有被加入白名單,一旦處於後臺就會被系統限制甚至幹掉,但使用APP的用戶才不聽你這些解釋——反正「我」就要你的APP能如期正常運行
  • 關於Linux進程你所需要知道的一切
    進程的類型在 Linux 中主要有兩種類型的進程: 前臺進程(也稱為交互式進程) - 這些進程由終端會話初始化和控制。換句話說,需要有一個連接到系統中的用戶來啟動這樣的進程;它們不是作為系統功能/服務的一部分自動啟動。
  • 騰訊安全揭秘惡意利用進程保活病毒應用攻擊
    Android為應用提供的各種應用進程常駐的正常接口,正在成為惡意應用開發者新的「保護傘」。惡意開發者通過惡意利用Android進程保活製造了大量流氓應用,不僅給用戶帶來資費受損、隱私洩露的嚴重後果,更導致手機設備電池快速消耗以及手機卡頓等現象,破壞了用戶使用安卓設備的用戶體驗。
  • Android進程管理:Framework層概念
    我們都知道Android中存在四大組件:Activity、Service、BroadcastReceiver和ContentProvider,那麼為什麼這樣劃分呢,一個主要的原因是在四大組件的啟動階段,如果宿主進程沒有正在運行,那麼系統服務進程system_server將會先拉起進程,然後才會繼續啟動組件。
  • 關於Android的.so文件你所需要知道的
    早期的Android系統幾乎只支持ARMv5的CPU架構,你知道現在它支持多少種嗎?7種!
  • Android進程管理:Framework層概念
    轉自《 安卓尖端技術研究》上一篇文章從Native角度講解了android進程管理的相關概念,本文將繼續從上層的Framework中的進程啟動、銷毀場景和優先級處理我們都知道Android中存在四大組件:Activity、Service、BroadcastReceiver和ContentProvider,那麼為什麼這樣劃分呢,一個主要的原因是在四大組件的啟動階段,如果宿主進程沒有正在運行,那麼系統服務進程system_server將會先拉起進程,然後才會繼續啟動組件。
  • 最全Android6.0及以上系統APP保活總結和實現,附進程常駐開源Demo
    接下來,我們需要了解下Android系統回收內存中的進程所依據的規則:進程在內存中時活動主要有五種狀態,即前臺進程、可見進程、服務進程、後臺進程、空進程,這幾種狀態的進程優先級由高到低,oom_adj值由低到高(在ProcessList定義)。
  • Android進程間通信與逆向分析
    前言熟悉安卓開發的同學應該都知道構建IPC的流程,但從逆向工程的角度分析的卻比較少見。說到安卓跨進程通信/調用,就不得不提到AIDL和Binder,在逆向一個東西之前,首先需要了解它,因此本文也會先對其工作流程和工作原理進行介紹。AIDL 101AIDL是Google定義的一個接口定義語言,即Android Interface Definition Language。
  • Android關於Color你所知道的和不知道的一切
    2.不過作為一名在大學被顏色燻(陶)過四年的人,對顏色多少還是挺親切的(雖然當時挺討厭的)3.紀念也好,記錄也罷,為它寫篇總結也理所應當4.如果你覺得並不需要了解關於顏色的知識,那你可以將本文當做一篇科普文(出去跟人家吹吹牛還是夠用的)科普,可直接跳過。
  • Android 進程間通訊:深入理解 Messenger 的實現細節
    Diycode 技術社區管理員,熱愛開源並學習開源,熱衷於技術分享,目標是成為一名 T 型開發者。個人博客:http://www.jianshu.com/users/ec95b5891948,Github地址:https://github.com/D-clock。近一個半月因為工作變動的緣故,忙著交接工作和複習面試。
  • 關於在Android中使用CMake你所需要了解的一切
    本篇轉自 xong的文章,分享了關於關於在Android中使用CMake你所需要了解的一切的相關內容,一起來看看!希望大家喜歡。關於CMake的資料又很少,所以就有了本文。 在去年在公司還是實習的時候,老大丟給我一個.a靜態庫(當時並不知道這是一個靜態庫),很是好奇,很想知道.a是怎麼構建出來的,a和so的區別又是什麼,總之一大堆疑問,在轉正後,也嘗試過要構建一個.a靜態庫,無奈!百度到的都是用mk的咯,找到一篇CMake的,但竟然要去Linux下面編譯,還要什麼自定義工具鏈,總之,麻煩的一批。
  • 2018年Android保活方案效果統計
    雙Service方案也改成了應用被殺,任何後臺Service無法正常狀態運行4、提高Service優先級:只能一定程度上緩解Service被立馬回收1、AIDL方式單進程、雙進程方式保活Service2、降低oom_adj的值:常駐通知欄(可通過啟動另外一個服務關閉Notification,不對oom_adj值有影響)、使用
  • Android drawable微技巧,你所不知道的drawable的那些細節
    話說微技巧這個詞也是我自己發明的,因為drawable這個東西相信大家天天都在使用,每個人都再熟悉不過了,之所以叫微技巧就是對於這個我們再熟悉不過的技術,可能還有一些你所不知道的細節,那今天我們就來一起探究一下這些微小的細節吧。 大家都知道,在Android項目當中,drawable文件夾都是用來放置圖片資源的,不管是jpg、png、還是9.png,都可以放在這裡。