Android平台App进程优先级

转载请标明出处:http://blog.csdn.net/xx326664162/article/details/52351047 文章出自:薛瑄的博客
你也可以查看我的其他同类文章,也会让你有一定的收货

我们都知道Android手机上是可以安装很多App的,每一个App至少是会有一个进程的。创建进程是件麻烦而且耗资源的事情,Android为了让App启动的时候能更快,会把那么暂时不使用的App的进程缓存起来,但是内存是有限的啊,总不能让所有的进程都放在内存里边吧,所以Android有一个淘汰机制,会根据App的运行状态设置一个进程的优先级(oom_adj),然后根据内存的紧张程度,把那些优先级低(oom_adj值大)的进程kill掉,以保证其他的进程能够有足够的内存使用。

注意:这个oom_adj值,是根据当前APP的运行状态(前台,后台等等)动态改变的。

进程

  1. Zygote进程
    这个是Android框架的主要进程,所有的App进程以及系统服务进程SystemServer都是由Zygote进程Fork出来的

  2. App的主进程
    每一个App的运行都是在一个独立的进程,进程的名字就是App的packagename,这些进程都是从Zygote进程Fork出来的,并受AMS(ActivityManagerService)管理

  3. App的辅助进程
    可以允许App有多个进程,在AndroidManifest.xml里边配置android:process属性,就可以开启多进程,这些进程名字都是packagename:name这种,以区分是属于哪个App,我一般称之为辅助进程。但这些进程也都跟主进程一样,也是从Zygote进程Fork出来的,并受AMS管理

  4. Native进程
    Android除了使用Java,还有NDK,可以使用C/C++去开发,然后这里也是可以Fork出进程的,我一般称之为Native进程,Native进程可以不受AMS管理,自由度很大,本文暂且不讲

如何查看App进程优先级

对于App进程,因为受AMS管理,都会有一个oom_adj的文件记录着oom_adj的值,但是对于Native进程,那AMS就管不着了。

//oom_adj的值就是进程的优先级,
//查看oom_adj值
cat /proc/${pid}/oom_adj

下面以查看微信的oom_adj为例,先通过ps | grep com.tencent.mm得到进程名包含“com.tencent.mm”的微信进程pid。ps命令使用详解可参考这里。

Android平台App进程优先级_第1张图片

优先级

App进程的优先级是在com.android.server.am.ProcessList类里边定义的,这个类在Android的API里边是找不到的,要想看看里边的实现可以去Android SDK里边去找,位于${android-sdk-path}\sources\android-23\com\android\server\am\ProcessList.java

主要有这么几个优先级(oom_adj值):

  • UNKNOWN_ADJ = 16
    预留的最低级别,一般对于缓存的进程才有可能设置成这个级别

    Adjustment used in certain places where we don’t know it yet. (Generally this is something that is going to be cached, but we don’t know the exact value in the cached range to assign yet.)

  • CACHED_APP_MAX_ADJ = 15
    缓存进程,空进程,在内存不足的情况下就会优先被kill

    This is a process only hosting activities that are not visible, so it can be killed without any disruption.

  • CACHED_APP_MIN_ADJ = 9
    缓存进程,也就是空进程

  • SERVICE_B_ADJ = 8
    不活跃的进程

    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.

  • PREVIOUS_APP_ADJ = 7
    切换进程

    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.

  • HOME_APP_ADJ = 6
    与Home交互的进程

    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.

  • SERVICE_ADJ = 5
    有Service的进程

    This is a process holding an application service – killing it will not have much of an impact as far as the user is concerned.

  • HEAVY_WEIGHT_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.

  • BACKUP_APP_ADJ = 3
    正在备份的进程

    This is a process currently hosting a backup operation. Killing it is not entirely fatal but is generally a bad idea.

  • PERCEPTIBLE_APP_ADJ = 2
    可感知的进程,比如那种播放音乐

    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.

  • VISIBLE_APP_ADJ = 1
    可见进程

    This is a process only hosting activities that are visible to the user, so we’d prefer they don’t disappear.

  • FOREGROUND_APP_ADJ = 0
    前台进程
    This is the process running the current foreground app. We’d really rather not kill it!

  • PERSISTENT_SERVICE_ADJ = -11
    重要进程

    This is a process that the system or a persistent process has bound to, and indicated it is important.

  • PERSISTENT_PROC_ADJ = -12
    核心进程

    This is a system persistent process, such as telephony. Definitely don’t want to kill it, but doing so is not completely fatal.

  • SYSTEM_ADJ = -16
    系统进程

    The system process runs at the default adjustment.

  • NATIVE_ADJ = -17
    系统起的Native进程

    Special code for native processes that are not being managed by the system (so don’t have an oom adj assigned by the system).

在Android-18及以下空进程不叫CACHED_APP_MIN_ADJ ,叫HIDDEN_APP_MIN_ADJ,有这么点不一样,但是值都一样。

lowmemorykiller 机制

这个机制是在goldfish层面实现的,如果要想具体了解是怎么选择杀进程的话,需要去看Android drivers下的lowmemorykiller.c的代码。

编译过goldfish的人,应该知道它其实就是Linux kernel。因为Android是基于Linux的操作系统嘛,kernel其实也是跟着Linux kernel走的,只不过在里边需要开发(或者精简)一些专门针对Android的东西,lowmemorykiller 就是其中的一部分,是个驱动。至于为啥会被放在drivers/staging目录下,可以参考下这篇文章《小议Linux staging tree》

有这么几个关键点:

  1. 向系统注册lowmem_shrinker回调,当系统空闲内存不足时调用,去杀进程

  2. 把空闲内存低于多少,就去杀那个级别的进程,我称之为策略

  3. 杀的策略由application层根据内存状况指定,写入文件传递给驱动

  4. 根据策略会计算出min_score_adj

  5. 每个App进程都有oom_adj值,根据oom_adj值计算出oom_score_adj(得分值),oom_adj越大,oom_score_adj越高,高于min_score_adj的被列入死亡名单

  6. 在死亡名单里面选一个占内存最大的进程kill掉

所以当内存不足的时候,进程优先级低的(oom_adj越大的),占内存大的App进程将会被优先kill掉。

至于这个策略嘛,各个ROM可能不一致的,根据内存的大小,系统的严格程度来制定,但都只能分6个级别。

查看传给lowmemorykiller驱动的策略文件:
Android平台App进程优先级_第2张图片

  • minfree:列举6个内存阀值。
    比如:32768,61440,73728,129024,147456,184320

  • adj:列举6个oom_adj的级别。
    比如:0,1,2,3,9,15

这两个值相互对应,空闲内存低于多少的时候就kill那个级别的进程。

Android平台App进程优先级_第3张图片

当然有些ROM也会把这个参数定义在init.rc里边,开机后再写入minfree和adj文件,传递给驱动

trimApplications机制

仅仅只有低内存的情况下才去kill进程吗?很明显不是,对于App的运行是有很多状况的,比如Crash、App退出、系统清理、卸载。

trimApplications是一个方法,定义在ActivityManagerService里边,Android API里边也是没有这个类的,得去SDK里边找,位于${android-sdk-path}\sources\android-23\com\android\server\am\ActivityManagerService.java

这个机制大概也有几个关键点:

  1. package已被卸载的无用进程会被Kill
  2. persistent的App会被优先照顾,进程优先级设置为
    PERSISTENT_PROC_ADJ=-12
  3. Activity仅在主进程跑的App会被认为是高权重进程,HEAVY_WEIGHT_APP_ADJ=4
  4. 只有前台进程才是FOREGROUND_APP_ADJ=0,前台进程不会被杀
  5. 每当Activity、Service等生命周期发生变化的时候都会引起进程的oo_adj的调整
  6. 进程里边没有任何的Activity的存在优先被杀
  7. 空进程最容易被杀

如何提高后台App进程的优先级

其实Android框架的思想是很好的,对于空的进程,没事干的进程直接kill掉,对于用户体验来讲是不会有影响的,但是往往我们的App都会有推送这个功能,恰巧GCM(Google Cloud Messaging)在国内又不能用,所以很多情况下我们也会希望App在后台的时候也尽量不要被杀。

可以考虑把后台App进程的优先级提高,下面有几个方法:

  • AndroidManifest.xml中配置persistent属性
"App"
android:persistent="true"
android:label="@string/dialerIconLabel"
android:icon="@drawable/ic_launcher_phone">
  • 重载back按键事件,让activity在后台运行,不要Destory
  • 开Service,并设置前台运行方式
  • 与NotificationManager交互,让进程变成可感知进程
  • 发送/接收广播,别让自己变成空进程

很明显这会消耗更多的电量,也有点流氓,有些需求也许本就不该做。

参考:

Android平台App进程优先级

微信Android客户端后台保活经验分享

你可能感兴趣的:(Android)