Android 進程保活

2020-08-12 16:31:36

早前,我在知乎上回答了這樣一個問題:怎麼讓 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進程被殺,需要儘可能的釋放一些不用的資源,尤其是圖片、音視訊之類的

總結

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

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

補充更新 (2016-04-20)

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

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

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

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

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

內容如下

Android Low Memory Killer

Low Memory Killer的原理

  在Android中,即使當使用者退出應用程式之後,應用程式的進程也還是存在於系統中,這樣是爲了方便程式的再次啓動,但是這樣的話,隨着開啓的程式數量的增加,系統的記憶體會變得不足,就需要殺掉一部分進程以釋放記憶體空間。至於是否需要殺死一些進程和哪些進程需要被殺死,是通過Low Memory Killer機制 機製來進行判定的。

  Android的Low Memory Killer基於Linux的OOM機制 機製,在Linux中,記憶體是以頁面爲單位分配的,當申請頁面分配時如果記憶體不足會通過以下流程選擇bad進程來殺掉從而釋放記憶體:

alloc_pages -> out_of_memory() -> select_bad_process() -> badness()

  在Low Memory Killer中通過進程的oom_adj與佔用記憶體的大小決定要殺死的進程,oom_adj越小越不容易被殺死。

  Low Memory Killer Driver在使用者空間指定了一組記憶體臨界值及與之一一對應的一組oom_adj值,當系統剩餘記憶體位於記憶體臨界值中的一個範圍內時,如果一個進程的oom_adj值大於或等於這個臨界值對應的oom_adj值就會被殺掉。

  可以通過修改/sys/module/lowmemorykiller/parameters/minfree與/sys/module/lowmemorykiller/parameters/adj來改變記憶體臨界值及與之對應的oom_adj值。minfree中數值的單位是記憶體中的頁面數量,一般情況下一個頁面是4KB。
  比如如果向/sys/module/lowmemorykiller/parameters/adj寫入0,8,向/sys/module/lowmemorykiller/parameters/minfree中寫入1024,4096,假設一個頁面大小爲4KB,這樣當系統空閒記憶體位於1024*4~4096*4KB之間時oom_adj大於等於8的進程就會被殺掉。

  在lowmemorykiller.c中定義了閾值表的預設值,可以通過init.rc自定義:

 

static int lowmem_adj[6] = {
        0,
        1,
        6,
        12,
};
static int lowmem_adj_size = 4;
static size_t lowmem_minfree[6] = {
        3 * 512,        /* 6MB */
        2 * 1024,       /* 8MB */
        4 * 1024,       /* 16MB */
        16 * 1024,      /* 64MB */
};
static int lowmem_minfree_size = 4; 

 

  在init.rc中定義了init進程的oom_adj爲-16,不可能會被殺死(init的PID是1):

on early-init
    # Set init and its forked children's oom_adj.
    write /proc/1/oom_adj -16

  在Linux中有一個kswapd的內核執行緒,當linux回收記憶體分頁的時候,kswapd執行緒將會遍歷一張shrinker鏈表,並執行回撥,定義如下:

/*
 * A callback you can register to apply pressure to ageable caches.
 *
 * 'shrink' is passed a count 'nr_to_scan' and a 'gfpmask'.  It should
 * look through the least-recently-used 'nr_to_scan' entries and
 * attempt to free them up.  It should return the number of objects
 * which remain in the cache.  If it returns -1, it means it cannot do
 * any scanning at this time (eg. there is a risk of deadlock).
 *
 * The 'gfpmask' refers to the allocation we are currently trying to
 * fulfil.
 *
 * Note that 'shrink' will be passed nr_to_scan == 0 when the VM is
 * querying the cache size, so a fastpath for that case is appropriate.
*/
struct shrinker {
    int (*shrink)(int nr_to_scan, gfp_t gfp_mask);
    int seeks;      /* seeks to recreate an obj */

    /* These are for internal use */
    struct list_head list;
    long nr;        /* objs pending delete */
};
#define DEFAULT_SEEKS 2 /* A good number if you don't know better. */
extern void register_shrinker(struct shrinker *);
extern void unregister_shrinker(struct shrinker *);

 

  通過register_shrinker與unregister_shrinker向shrinker鏈表中新增或移除回撥。當註冊Shrinker後就可以在回收記憶體分頁時按自己定義的規則釋放記憶體。

  Android Low Memory Killer的程式碼在drivers/staging/android/lowmemorykiller.c中,通過以下程式碼在模組初始化時註冊Shrinker:

static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask);
 
static struct shrinker lowmem_shrinker = {
        .shrink = lowmem_shrink,
        .seeks = DEFAULT_SEEKS * 16
};

static int __init lowmem_init(void)
{
        register_shrinker(&lowmem_shrinker);
        return 0;
}

static void __exit lowmem_exit(void)
{
        unregister_shrinker(&lowmem_shrinker);
}

module_init(lowmem_init);
module_exit(lowmem_exit);

 

  這樣就可以在回收記憶體分頁時呼叫lowmem_shrink函數。

Low Memory Killer的實現

  lowmem_shrink的定義如下:

static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask)
{
        struct task_struct *p;
        struct task_struct *selected = NULL;
        int rem = 0;
        int tasksize;
        int i;
        int min_adj = OOM_ADJUST_MAX + 1;
        int selected_tasksize = 0;
        int selected_oom_adj;
        int array_size = ARRAY_SIZE(lowmem_adj);
        int other_free = global_page_state(NR_FREE_PAGES);
        int other_file = global_page_state(NR_FILE_PAGES);

        if (lowmem_adj_size < array_size)
                array_size = lowmem_adj_size;
        if (lowmem_minfree_size < array_size)
                array_size = lowmem_minfree_size;
        for (i = 0; i < array_size; i++) {
                if (other_free < lowmem_minfree[i] &&
                    other_file < lowmem_minfree[i]) {
                        min_adj = lowmem_adj[i];
                        break;
                }
        }
        if (nr_to_scan > 0)
                lowmem_print(3, "lowmem_shrink %d, %x, ofree %d %d, ma %d\n",
                             nr_to_scan, gfp_mask, other_free, other_file,
                             min_adj);
        rem = global_page_state(NR_ACTIVE_ANON) +
                global_page_state(NR_ACTIVE_FILE) +
                global_page_state(NR_INACTIVE_ANON) +
                global_page_state(NR_INACTIVE_FILE);
        if (nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) {
                lowmem_print(5, "lowmem_shrink %d, %x, return %d\n",
                             nr_to_scan, gfp_mask, rem);
                return rem;
        }
        selected_oom_adj = min_adj;

        read_lock(&tasklist_lock);
        for_each_process(p) {
                struct mm_struct *mm;
                int oom_adj;

                task_lock(p);
                mm = p->mm;
                if (!mm) {
                        task_unlock(p);
                        continue;
                }
                oom_adj = mm->oom_adj;
                if (oom_adj < min_adj) {
                        task_unlock(p);
                        continue;
                }
                tasksize = get_mm_rss(mm);
                task_unlock(p);
                if (tasksize <= 0)
                        continue;
                if (selected) {
                        if (oom_adj < selected_oom_adj)
                                continue;
                        if (oom_adj == selected_oom_adj &&
                            tasksize <= selected_tasksize)
                                continue;
                }
                selected = p;
                selected_tasksize = tasksize;
                selected_oom_adj = oom_adj;
                lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n",
                             p->pid, p->comm, oom_adj, tasksize);
        }
        if (selected) {
                lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n",
                             selected->pid, selected->comm,
                             selected_oom_adj, selected_tasksize);
                force_sig(SIGKILL, selected);
                rem -= selected_tasksize;
        }
        lowmem_print(4, "lowmem_shrink %d, %x, return %d\n",
                     nr_to_scan, gfp_mask, rem);
        read_unlock(&tasklist_lock);
        return rem;
}

  分開來看這段程式碼,首先取得記憶體閾值表的大小,取閾值表陣列大小與lowmem_adj_size,lowmem_minfree_size的較小值,然後通過globa_page_state獲得當前剩餘記憶體的大小,然後跟記憶體閾值表中的閾值相比較獲得min_adj與selected_oom_adj:

int array_size = ARRAY_SIZE(lowmem_adj);
int other_free = global_page_state(NR_FREE_PAGES);
int other_file = global_page_state(NR_FILE_PAGES);

if (lowmem_adj_size < array_size)
        array_size = lowmem_adj_size;
if (lowmem_minfree_size < array_size)
        array_size = lowmem_minfree_size;
for (i = 0; i < array_size; i++) {
    if (other_free < lowmem_minfree[i] && other_file < lowmem_minfree[i]) {
         min_adj = lowmem_adj[i];
         break;
    }
}
selected_oom_adj = min_adj;

 

  遍歷所有進程找到oom_adj>min_adj並且佔用記憶體大的進程:

 

read_lock(&tasklist_lock);
for_each_process(p) {
    struct mm_struct *mm;
    int oom_adj;

    task_lock(p);
    mm = p->mm;
    if (!mm) {
        task_unlock(p);
        continue;
    }
    oom_adj = mm->oom_adj;
    //獲取task_struct->struct_mm->oom_adj,如果小於警戒值min_adj不做處理
    if (oom_adj < min_adj) {
        task_unlock(p);
        continue;
    }
    //如果走到這裏說明oom_adj>=min_adj,即超過警戒值
    //獲取記憶體佔用大小,若<=0,不做處理
    tasksize = get_mm_rss(mm);
    task_unlock(p);
    if (tasksize <= 0)
        continue;
    //如果之前已經先擇了一個進程,比較當前進程與之前選擇的進程的oom_adj與記憶體佔用大小,如果oom_adj比之前選擇的小或相等而記憶體佔用比之前選擇的進程小,不做處理。
    if (selected) {
        if (oom_adj < selected_oom_adj)
            continue;
        if (oom_adj == selected_oom_adj &&
            tasksize <= selected_tasksize)
            continue;
    }
    //走到這裏表示當前進程比之前選擇的進程oom_adj大或相等但佔用記憶體大,選擇當前進程
    selected = p;
    selected_tasksize = tasksize;
    selected_oom_adj = oom_adj;
    lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n",
                 p->pid, p->comm, oom_adj, tasksize);
}

 

  如果選擇出了符合條件的進程,發送SIGNAL信號Kill掉:

 

if (selected) {
    lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n",
                 selected->pid, selected->comm,
                 selected_oom_adj, selected_tasksize);
    force_sig(SIGKILL, selected);
    rem -= selected_tasksize;
}

 

oom_adj與上層Process Importance的關係

  我們知道,在上層進程按重要性可以分爲:Foreground process,Visible process,Service process,Background process與Empty process,那麼這些重要性怎麼與Low Memory Killer中的oom_adj對應起來的呢?

  在ActivityManager.RunningAppProcessInfo中我們可以看到如下關於importance的定義:

/**
 * Constant for {@link #importance}: this is a persistent process.
 * Only used when reporting to process observers.
 * @hide
 */
public static final int IMPORTANCE_PERSISTENT = 50;

/**
 * Constant for {@link #importance}: this process is running the
 * foreground UI.
 */
public static final int IMPORTANCE_FOREGROUND = 100;

/**
 * Constant for {@link #importance}: this process is running something
 * that is actively visible to the user, though not in the immediate
 * foreground.
 */
public static final int IMPORTANCE_VISIBLE = 200;

/**
 * Constant for {@link #importance}: this process is running something
 * that is considered to be actively perceptible to the user.  An
 * example would be an application performing background music playback.
 */
public static final int IMPORTANCE_PERCEPTIBLE = 130;

/**
 * Constant for {@link #importance}: this process is running an
 * application that can not save its state, and thus can't be killed
 * while in the background.
 * @hide
 */
public static final int IMPORTANCE_CANT_SAVE_STATE = 170;

/**
 * Constant for {@link #importance}: this process is contains services
 * that should remain running.
 */
public static final int IMPORTANCE_SERVICE = 300;

/**
 * Constant for {@link #importance}: this process process contains
 * background code that is expendable.
 */
public static final int IMPORTANCE_BACKGROUND = 400;

/**
 * Constant for {@link #importance}: this process is empty of any
 * actively running code.
 */
public static final int IMPORTANCE_EMPTY = 500;

 

  這些常數表示了Process的Importance等級,而在ProcessList中我們會發現關於adj的一些定義:

// This is a process only hosting activities that are not visible,
// so it can be killed without any disruption.
static final int HIDDEN_APP_MAX_ADJ = 15;
static int HIDDEN_APP_MIN_ADJ = 9;

// The B list of SERVICE_ADJ -- these are the old and decrepit
// services that aren't as shiny and interesting as the ones in the A list.
static final int SERVICE_B_ADJ = 8;

// This is the process of the previous application that the user was in.
// This process is kept above other things, because it is very common to
// switch back to the previous app.  This is important both for recent
// task switch (toggling between the two top recent apps) as well as normal
// UI flow such as clicking on a URI in the e-mail app to view in the browser,
// and then pressing back to return to e-mail.
static final int PREVIOUS_APP_ADJ = 7;

// This is a process holding the home application -- we want to try
// avoiding killing it, even if it would normally be in the background,

// because the user interacts with it so much.
static final int HOME_APP_ADJ = 6;

// This is a process holding an application service -- killing it will not
// have much of an impact as far as the user is concerned.
static final int SERVICE_ADJ = 5;

// This is a process currently hosting a backup operation.  Killing it
// is not entirely fatal but is generally a bad idea.
static final int BACKUP_APP_ADJ = 4;

// This is a process with a heavy-weight application.  It is in the
// background, but we want to try to avoid killing it.  Value set in
// system/rootdir/init.rc on startup.
static final int HEAVY_WEIGHT_APP_ADJ = 3;

// This is a process only hosting components that are perceptible to the
// user, and we really want to avoid killing them, but they are not
// immediately visible. An example is background music playback.
static final int PERCEPTIBLE_APP_ADJ = 2;

// This is a process only hosting activities that are visible to the
// user, so we'd prefer they don't disappear.
static final int VISIBLE_APP_ADJ = 1;

// This is the process running the current foreground app.  We'd really
// rather not kill it!
static final int FOREGROUND_APP_ADJ = 0;

// This is a system persistent process, such as telephony.  Definitely
// don't want to kill it, but doing so is not completely fatal.
static final int PERSISTENT_PROC_ADJ = -12;

// The system process runs at the default adjustment.
static final int SYSTEM_ADJ = -16;

 

  我們可以看到:

static final int PREVIOUS_APP_ADJ = 7;
static final int HOME_APP_ADJ = 6;

  並不是所有的Background process的等級都是相同的。

  關於ADJ與Importance的值都找到了,那麼它們是怎麼對應起來的呢?Activity實際是由ActivityManagerService來管理的,在ActivityManagerService中我們可以找到以下函數:

static int oomAdjToImportance(int adj, ActivityManager.RunningAppProcessInfo currApp) {
    if (adj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
        if (currApp != null) {
            currApp.lru = adj - ProcessList.HIDDEN_APP_MIN_ADJ + 1;
        }
        return ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
    } else if (adj >= ProcessList.SERVICE_B_ADJ) {
        return ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
    } else if (adj >= ProcessList.HOME_APP_ADJ) {
        if (currApp != null) {
            currApp.lru = 0;
        }
        return ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
    } else if (adj >= ProcessList.SERVICE_ADJ) {
        return ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
    } else if (adj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
        return ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE;
    } else if (adj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
        return ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE;
    } else if (adj >= ProcessList.VISIBLE_APP_ADJ) {
        return ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
    } else {
        return ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
    }
}

 

  在這個函數中實現了根據adj設定importance的功能。

  我們還可以看到SERVICE還分爲SERVICE_B_ADJ與SERVICE_ADJ,等級是不一樣的,並不是所有Service的優先順序都比Background process的優先順序高。當呼叫Service的startForeground後,Service的importance就變爲了IMPORTANCE_PERCEPTIBLE(在記憶中曾經將Service設定爲foreground並列印出其importance的值與IMPORTANCE_PERCEPTIBLE相等),對應的adj是PERCEPTIBLE_APP_ADJ,即2,已經很難被系統殺死了。

 

// This is a system persistent process, such as telephony.  Definitely
// don't want to kill it, but doing so is not completely fatal.
static final int PERSISTENT_PROC_ADJ = -12;

// The system process runs at the default adjustment.
static final int SYSTEM_ADJ = -16;

 

  像電話等進程的adj爲-12已基本不可能被殺死了,而在前面已經看到了,init.rc中將init進程的oom_adj設定爲了-16,已經是永生進程了。

 

相關鏈接:

lowermemorykiller.txt

lowermemorykiller.c

shrinker_list

 




鏈接:https://www.jianshu.com/p/63aafe3c12af