1. 程式人生 > >關於 Android 程序保活,你所需要知道的一切

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

早前,我在知乎上回答了這樣一個問題:怎麼讓 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之間的喚醒路徑的時候,發現了兩個問題:

  1. 很多推送SDK也存在喚醒app的功能
  2. 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),但你的程序優先順序又是高於普通後臺程序的。那麼如何利用系統的漏洞呢,大致的實現思路和程式碼如下:

  • 思路一:API < 18,啟動前臺Service時直接傳入new Notification();
  • 思路二:API >= 18,同時啟動兩個id相同的前臺Service,然後再將後啟動的Service做stop處理;

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越大,表示此程序優先順序越低,越容易被殺回收;越小,表示程序優先順序越高,越不容易被殺回收
  • 普通app程序的oom_adj>=0,系統程序的oom_adj才可能<0

那麼我們如何檢視程序的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分別是多少,又是如何掛鉤起來的呢?推薦大家閱讀下面這篇文章:

總結(文末有福利)

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

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

補充更新 (2016-04-20)

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

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

相關推薦

關於 Android 程序需要知道一切

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

Android 程序Service程序常駐

關於 Android 平臺的程序保活這一塊,想必是所有 Android 開發者矚目的內容之一。你到網上搜 Android 程序保活,可以搜出各種各樣神乎其技的做法,絕大多數都是極其不靠譜。前段時間,Github還出現了一個很火的“黑科技”程序保活庫,聲稱可以做到

關於Android Studio裡的Gradle需要知道的都在這裡了

前言 你是不是有這種感覺,每當你使用Android Studio的時候,總會被她的優雅和便捷深深的吸引,但是一旦開啟build.gradle檔案就有一種想死的衝動,這尼瑪都是什麼啊,老子看不懂啦(ノಠ益ಠ)ノ彡┻━┻ ,不要著急,這篇文章就是來解救你的,看完這篇文章,你

關於字符編碼需要知道的(ASCII,Unicode,Utf-8,GB2312…)

tle cati http char 表示 編寫 編號 小端 調用 字符編碼的問題看似很小,經常被技術人員忽視,但是很容易導致一些莫名其妙的問題。這裏總結了一下字符編碼的一些普及性的知識,希望對大家有所幫助。 還是得從ASCII碼說起 說到字符編碼,不得不說ASCII碼的簡

Android drawable微技巧知道的drawable的那些細節

好像有挺久時間沒更新部落格了,最近我為了準備下一個系列的部落格, 也是花了很長的時間研讀原始碼。很遺憾的是,下一個系列的部落格我可能還要再過一段時間才能寫出來,那麼為了不至於讓大家等太久,今天就給大家更新一篇單篇的文章,講一講android drawable方面的微技巧。 話說

關於字元編碼需要知道的(ASCII,Unicode,Utf-8,GB2312…)

總結: ascii碼佔用一個位元組 gb2312(gbk是其擴充套件)佔用兩個位元組 Unicode是一種編碼方式,utf-8,utf-16是其具體實現,決定每個字元位元組數量,uft-8為1-4,utf-16為2-4,Windows使用utf-16,Linux使用

關於字元編碼需要知道

字元編碼的問題看似很小,經常被技術人員忽視,但是很容易導致一些莫名其妙的問題。這裡總結了一下字元編碼的一些普及性的知識,希望對大家有所幫助。 還是得從ASCII碼說起 說到字元編碼,不得不說ASCII碼的簡史。計算機一開始發明的時候是用來解決數字計算的問題,後來人們發現,計

Android Context完全解析知道的Context的各種細節

前幾篇文章,我也是費勁心思寫了一個ListView系列的三部曲,雖然在內容上可以說是絕對的精華,但是很多朋友都表示看不懂。好吧,這

android程序實踐(根據鴻洋大神彙總本人忘性大備份下)

前言程序保活的關鍵點有兩個,一個是程序優先順序的理解,優先順序越高存活機率越大。二是弄清楚哪些場景會導致程序會kill,然後採取下面的策略對各種場景進行優化:提高程序的優先順序在程序被kill之後能夠喚醒程序優先順序Android一般的程序優先順序劃分:1.前臺程序 (Foreground process)2

Android程序(最新)帶淺析這幾種可行性的方案

1.概述   據前人驗證,在沒有白名單的情況下,安卓系統要做一個任何情況下都不被殺死的應用是基本不可能的,但是我們可以做到應用基本不被殺死,如果殺死可以立即復活.經過上網查詢,程序常駐的方案眾說紛紜,但是很多的方案都是不靠譜的或不是最好的,結合很多資料,今天總結一下And

android程序兩年實戰經驗(已經上線使用)

程序保活參考: https://www.jianshu.com/p/53c4d8303e19 https://github.com/08carmelo/android-keeplive 以上地址這個我是通過鴻洋的公眾號看到的,我們公司做的是VPN撥號必須要求app保持後臺執行,上面連

Android程序相關實踐

最近測試APP時接到個需求:1畫素保活 打眼一看,應該跟安卓程序有關係,索性找點詳細的資料來了解下: 系統什麼時候殺掉一個程序?為什麼殺掉這個程序?怎樣最大程度保活一個程序?詳細的講解可以參考這個連結------https://www.cnblogs.com/Doing-what-I-love/p/553

Android 程序資料

現在發現App在後臺執行越來越難了。App在華為手機後臺死的非常快,之前看網上說華為有白名單,網上也通過改包名的方式來驗證了。但是半信半疑的,直到諮詢了華為的客服給了一個郵箱 [email protected],回覆的資料 應用加白名單簡化流程v0.1

【騰訊Bugly乾貨分享】Android程序招式大全

【騰訊Bugly乾貨分享】Android程序保活招式大全 本文來自於騰訊bugly開發者社群,非經作者同意,請勿轉載,原文地址:http://dev.qq.com/topic/57ac4a0ea374c75371c08ce8 作者:騰訊——張興華 目前市面上的應用,貌似除了微信和手Q都會

Android程序招數概覽

Android中的程序保活應該分為兩個方面: 提高程序的優先順序,減少被系統殺死的可能性 在程序已經被殺死的情況下,通過一些手段來重新啟動應用程序 本文針對這兩方面來程序闡述,並給出相應的示例。其實主要也是在前人的基礎上做了一個總結,並進行了一些實踐。 閱讀本

android程序實戰(已經上線使用)

https://github.com/08carmelo/android-keeplive 這個我是通過鴻洋的公眾號看到的,我們公司做的是vpn撥號必須要求app保持後臺執行,上面介紹的很詳細,我用的github中使用的程式碼 這個具體需要你自己看 我使用後其實還是有

Android程序招式大全

作者:騰訊——張興華目前市面上的應用,貌似除了微信和手Q都會比較擔心被使用者或者系統(廠商)殺死問題。本文對 Android 程序拉活進行一個總結。Android 程序拉活包括兩個層面:A. 提供程序優先順序,降低程序被殺死的概率B. 在程序被殺死後,進行拉活本文下面就從這兩

關於 Linux 程序需要知道一切

 在這篇指南中,我們會逐步對程序做基本的瞭解,然後簡要看看如何用特定命令管理 Linux 程序。 程序process是指正在執行的程式;是程式正在執行的一個例項。它由程式指令,和從檔案、其它程式中讀取的資料或系統使用者的輸入組成。 程序的型別 在 Linux 中主要

Android 程序資料彙總與華為白名單那些事

現在發現App在後臺執行越來越難了。App在華為手機後臺死的非常快,之前看網上說華為有白名單,網上也通過改包名的方式來驗證了。但是半信半疑的,直到諮詢了華為的客服給了一個郵箱 [email protected],回覆的資料 應用加白名單簡化流程v0.1

Android程序(常駐記憶體)

Android將程序分為6個等級,它們按優先順序順序由高到低依次是:  1.前臺程序( FOREGROUND_APP);  2.可視程序(VISIBLE_APP );  3. 次要服務程序(SECONDARY_SERVER );  4.後臺程序 (HIDDEN_APP);