android進程保活實踐

前言

進程保活的關鍵點有兩個,一個是進程優先級的理解,優先級越高存活幾率越大。二是弄清楚哪些場景會導致進程會kill,然后采取下面的策略對各種場景進行優化:

  1. 提高進程的優先級
  2. 在進程被kill之后能夠喚醒

進程優先級

Android一般的進程優先級劃分:
1.前臺進程 (Foreground process)
2.可見進程 (Visible process)
3.服務進程 (Service process)
4.后臺進程 (Background process)
5.空進程 (Empty process)
這是一種粗略的劃分,進程其實有一種具體的數值,稱作oom_adj,注意:數值越大優先級越低:


image.png
  • 紅色部分是容易被回收的進程,屬于android進程
  • 綠色部分是較難被回收的進程,屬于android進程
  • 其他部分則不是android進程,也不會被系統回收,一般是ROM自帶的app和服務才能擁有

如何查看某個進程的oom_adj數值呢?
oom_adj 存儲在proc/PID/oom_adj文件中,其中PID是進程的id,直接 adb shell進入手機根目錄查看這個文件即可。

演示一下:以我自己的項目為例,app中有兩個進程,一個是主進程,另一個是運行service的進程取名為:remote。首先用android studio查看每個進程的PID:


image.png

然后分別查看app在前臺,app退到后臺,這2中場景主進程的oom_adj數值:


image.png

可見,當app在前臺時 oom_adj = 0,對應上面的表格是前臺進程。
當app退到后臺時,oom_adj = 6,對應后臺進程。

然后查看運行著service的進程:


image.png

ok,知道了進程優先級的概念以及如何查看優先級,我們就可以對app進程優化,然后通過查看這個數值判斷我們的優化是否有效果。

進程被kill的場景

1.點擊home鍵使app長時間停留在后臺,內存不足被kill

處理這種情況前提是你的app至少運行了一個service,然后通過Service.startForeground() 設置為前臺服務,可以將oom_adj的數值由4降低到1,大大提高存活率。

  • 要注意的是android4.3之后Service.startForeground() 會強制彈出通知欄,解決辦法是再啟動一個service和推送共用一個通知欄,然后stop這個service使得通知欄消失。
  • Android 7.1之后google修復這個bug,目前沒有解決辦法
    下面的代碼放到你的service的onStartCommand方法中:
        //設置service為前臺服務,提高優先級
        if (Build.VERSION.SDK_INT < 18) {
            //Android4.3以下 ,此方法能有效隱藏Notification上的圖標
            service.startForeground(GRAY_SERVICE_ID, new Notification());
        } else if(Build.VERSION.SDK_INT>18 && Build.VERSION.SDK_INT<25){
            //Android4.3 - Android7.0,此方法能有效隱藏Notification上的圖標
            Intent innerIntent = new Intent(service, GrayInnerService.class);
            service.startService(innerIntent);
            service.startForeground(GRAY_SERVICE_ID, new Notification());
        }else{
            //Android7.1 google修復了此漏洞,暫無解決方法(現狀:Android7.1以上app啟動后通知欄會出現一條"正在運行"的通知消息)
            service.startForeground(GRAY_SERVICE_ID, new Notification());
        }

經過改進之后,再來看下這個后臺service進程的oom_adj,發現被提升為前臺進程。


image.png

image.png
2.在大多數國產手機下,進入鎖屏狀態一段時間,省電機制會kill后臺進程

這種情況和上面不太一樣,是很過國產手機rom自帶的優化,當鎖屏一段時間之后,即使手機內存夠用為了省電,也會釋放掉一部分內存。

策略:注冊廣播監聽鎖屏和解鎖事件, 鎖屏后啟動一個1像素的透明Activity,這樣直接把進程的oom_adj數值降低到0,0是android進程的最高優先級。 解鎖后銷毀這個透明Activity。這里我把這個Activity放到:remote進程也就是我那個后臺服務進程,當然你也可以放到主進程,看你打算?;钅膫€進程。


image.png

我們可以寫一個KeepLiveManager來負責接收廣播,維護這個Activity的常見和銷毀,注意鎖屏廣播和解鎖分別是:ACTION_SCREEN_OOF和ACTION_USER_PRESENT,并且只能通過動態注冊來綁定,并且是綁定到你的后臺service里面,onCreate綁定,onDestroy里面解綁

配好之后把手機鎖屏,看下:remote進程的oom_adj:


image.png
3. 用戶手動釋放內存:包括手機自帶清理工具,和第三方app(360,獵豹清理大師等)

清理內存軟件會把 優先級低于 前臺進程(oom_adj = 0)的所有進程放入清理列表,而當我們打開了清理軟件就意味著其他app不可能處于前臺。所以說理論上可以kill任何app。
以360安全衛士為例,打開內存清理:


image.png

因此這類場景唯一的處理辦法就是加入 手機rom 白名單,比如你打開小米,魅族的權限管理 -> 自啟動管理可以看到 QQ,微信,天貓默認被勾選,這就是廠商合作。那我們普通app可以這么做:在app的設置界面加一個選項,提示用戶自己去勾選自啟動,我封裝了一個工具類給出國內各廠商的自啟動的Intent跳轉方法:

/**
 * Created by carmelo on 2018/3/17.
 * 國內手機廠商白名單跳轉工具類
 */

public class SettingUtils {

    public static void enterWhiteListSetting(Context context){
        try {
            context.startActivity(getSettingIntent());
        }catch (Exception e){
            context.startActivity(new Intent(Settings.ACTION_SETTINGS));
        }
    }

    private static Intent getSettingIntent(){

        ComponentName componentName = null;

        String brand = android.os.Build.BRAND;

        switch (brand.toLowerCase()){
            case "samsung":
                componentName = new ComponentName("com.samsung.android.sm",
                        "com.samsung.android.sm.app.dashboard.SmartManagerDashBoardActivity");
                break;
            case "huawei":
                componentName = new ComponentName("com.huawei.systemmanager",
                        "com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity");
                break;
            case "xiaomi":
                componentName = new ComponentName("com.miui.securitycenter",
                        "com.miui.permcenter.autostart.AutoStartManagementActivity");
                break;
            case "vivo":
                componentName = new ComponentName("com.iqoo.secure",
                        "com.iqoo.secure.ui.phoneoptimize.AddWhiteListActivity");
                break;
            case "oppo":
                componentName = new ComponentName("com.coloros.oppoguardelf",
                        "com.coloros.powermanager.fuelgaue.PowerUsageModelActivity");
                break;
            case "360":
                componentName = new ComponentName("com.yulong.android.coolsafe",
                        "com.yulong.android.coolsafe.ui.activity.autorun.AutoRunListActivity");
                break;
            case "meizu":
                componentName = new ComponentName("com.meizu.safe",
                        "com.meizu.safe.permission.SmartBGActivity");
                break;
            case "oneplus":
                componentName = new ComponentName("com.oneplus.security",
                        "com.oneplus.security.chainlaunch.view.ChainLaunchAppListActivity");
                break;
            default:
                break;
        }

        Intent intent = new Intent();
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if(componentName!=null){
            intent.setComponent(componentName);
        }else{
            intent.setAction(Settings.ACTION_SETTINGS);
        }
        return intent;
    }
}

補充幾點:

  • 額外說下 自啟動是什么意思? 網上都是小米開啟自啟動就是加入白名單,其實根本不是這樣的,經過實測就算app勾選自啟動也會被內存優化加速清理掉,只不過進程會在半分鐘后復活。
  • 除了還有自啟動還有一個設置就是電池管理,比如小米的神隱模式,這部分和自啟動不同的是它是管理app在鎖屏之后被省電機制殺死的場景,當然每家廠商也有對應的Intent跳轉路徑。
  • 如何查找不同廠商的設置界面跳轉Intent,比如上面的國內手機廠商白名單?

在酷安應用市場下載一個叫 當前Activity 的app,打開后可以看到當前界面的className,例如:


image.png

就找到了魅族MX4 pro 后臺權限的Activity。

進程喚醒

分兩種情況,一是主進程(含有Activity沒有service),這種進程由于內存不足被kill之后,用戶再次打開app系統會恢復到上次的Activity,這個不在本文話題之內。另一種是service的后臺進程被kill,可以通過service自有api來重啟service:

@Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //.....
        return START_STICKY;    // service被異常停止后,系統嘗試重啟service,不能保證100%重啟成功
    }

配好START_STICKY后,通過android studio 釋放進程的工具測試下,可以發現:remote進程被kill之后馬上重啟了:


2.gif

但它不是100%保證重啟成功,比如下面2種情況:(本人經過測試,這里就不放效果圖了)

  • Service 第一次被異常殺死后會在5秒內重啟,第二次被殺死會在10秒內重啟,第三次會在20秒內重啟,一旦在短時間內 Service 被殺死達到5次,則系統不再拉起。

  • 進程被取得 Root 權限的管理工具或系統工具通過 forestop 停止掉,無法重啟。

總結

本文通過兩種 提高進程優先級的方法,針對鎖屏 和非鎖屏模式下進程在后臺被kill的場景處理,把后臺進程優先級提升到可見級別,基本可以保證絕大多數場景不會被kill。另外,針對含有service的進程被kill給出了可喚醒的辦法。

后來收到不少留言評論,大多數都是講這個進程保活對很多手機沒有作用。我一直沒有回復,因為我們項目在使用這個進程保活策略時,同時也加入了進程存活時間的Log記錄機制,目的就是想看下有效果沒。后臺service的啟動就開啟計時器,以分鐘為單位不停寫入SharePreference,進程被kill這個值就是存活時間(min),同時記錄機型,Android版本等信息,以Exception的格式封裝上傳到bugly。由于是純手動分析數據很麻煩,最后取了1000條數據涵蓋了Android5.0-Android8.0,小米,華為,三星,oppo/vivo,金立等各種機型。

結論

  1. 和Android版本關系很大
    對于Android6.0以及以下的大部分機型還是有效果的,但是Android7.0和Android8.0基本上所有機型全部陣亡,大部分后臺進程在鎖屏后無法存活超過20分鐘。
    這個可以從Android 6.0,7.0和8.0的新特性看出一些端倪,google對于內存/電量使用越來越嚴格。


    image.png

2.和手機廠商關系比較大,測試結果顯示,oppo/vivo這兩家廠商進程?;钭罾щy,小米和三星比較寬松。其他的機型居中。

3.綜合結果是,鑒于目前國內大部分手機基本都到7.0了,進程?;顣簳r可以說是宣告失敗了。除了加入廠商白名單之外我不認為有真正可行的方案,畢竟google近幾個Android版本不停在系統層面限制,我們在app層沒有很大的發揮空間,so,之前的方案只能作為一個優化策略吧。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,572評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,071評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,409評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,569評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,360評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,895評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,123評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,643評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,559評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,742評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,981評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,363評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,622評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,354評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,707評論 2 370

推薦閱讀更多精彩內容