黄芪

首页 » 常识 » 问答 » Android后台杀死系列LowMemo
TUhjnbcbe - 2025/3/9 18:24:00
北京治疗白癜风的好医院 http://www.zherpaint.com/bzlf/kkjs/m/147.html

App操作影响进程优先级

本篇是Android后台杀死系列的第三篇,前面两篇已经对后台杀死注意事项,杀死恢复机制做了分析,本篇主要讲解的是Android后台杀死原理。相对于后台杀死恢复,LowMemoryKiller原理相对简单,并且在网上还是能找到不少资料的,不过,由于Android不同版本在框架层的实现有一些不同,网上的分析也多是针对一个Android版本,本文简单做了以下区分对比。

LowMemoryKiller(低内存杀手)是Andorid基于oomKiller原理所扩展的一个多层次oomKiller,OOMkiller(OutOfMemoryKiller)是在Linux系统无法分配新内存的时候,选择性杀掉进程,到oom的时候,系统可能已经不太稳定,而LowMemoryKiller是一种根据内存阈值级别触发的内存回收的机制,在系统可用内存较低时,就会选择性杀死进程的策略,相对OOMKiller,更加灵活。在详细分析其原理与运行机制之前,不妨自己想一下,假设让你设计一个LowMemoryKiller,你会如何做,这样一个系统需要什么功能模块呢?

进程优先级定义:只有有了优先级,才能决定先杀谁,后杀谁

进程优先级的动态管理:一个进程的优先级不应该是固定不变的,需要根据其变动而动态变化,比如前台进程切换到后台优先级肯定要降低

进程杀死的时机,什么时候需要挑一个,或者挑多个进程杀死

如何杀死

以上几个问题便是一个MemoryKiller模块需要的基本功能,Android底层采用的是Linux内核,其进程管理都是基于Linux内核,LowMemoryKiller也相应的放在内核模块,这也意味着用户空间对于后台杀死不可见,就像AMS完全不知道一个APP是否被后台杀死,只有在AMS唤醒APP的时候,才知道APP是否被LowMemoryKiller杀死过。其实LowmemoryKiller的原理是很清晰的,先看一下整体流程图,再逐步分析:

先记住两点:

LowMemoryKiller是被动杀死进程

Android应用通过AMS,利用proc文件系统更新进程信息

aname="kill"/a

Android应用进程优先级及oomAdj

Android会尽可能长时间地保持应用存活,但为了新建或运行更重要的进程,可能需要移除旧进程来回收内存,在选择要Kill的进程的时候,系统会根据进程的运行状态作出评估,权衡进程的“重要性“,其权衡的依据主要是四大组件。如果需要缩减内存,系统会首先消除重要性最低的进程,然后是重要性略逊的进程,依此类推,以回收系统资源。在Android中,应用进程划分5级(摘自Google文档):Android中APP的重要性层次一共5级:

前台进程(Foregroundprocess)

可见进程(Visibleprocess)

服务进程(Serviceprocess)

后台进程(Backgroundprocess)

空进程(Emptyprocess)

前台进程

用户当前操作所必需的进程。如果一个进程满足以下任一条件,即视为前台进程:

包含正在交互的Activity(resumed

包含绑定到正在交互的Activity的Service

包含正在“前台”运行的Service(服务已调用startForeground())

包含正执行一个生命周期回调的Service(onCreate()、onStart()或onDestroy())

包含一个正执行其onReceive()方法的BroadcastReceiver

通常,在任意给定时间前台进程都为数不多。只有在内存不足以支持它们同时继续运行这一万不得已的情况下,系统才会终止它们。此时,设备往往已达到内存分页状态,因此需要终止一些前台进程来确保用户界面正常响应。

可见进程

没有任何前台组件、但仍会影响用户在屏幕上所见内容的进程。如果一个进程满足以下任一条件,即视为可见进程:

包含不在前台、但仍对用户可见的Activity(已调用其onPause()方法)。例如,如果前台Activity启动了一个对话框,允许在其后显示上一Activity,则有可能会发生这种情况。

包含绑定到可见(或前台)Activity的Service。

可见进程被视为是极其重要的进程,除非为了维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程。

服务进程

正在运行已使用startService()方法启动的服务且不属于上述两个更高类别进程的进程。尽管服务进程与用户所见内容没有直接关联,但是它们通常在执行一些用户关心的操作(例如,在后台播放音乐或从网络下载数据)。因此,除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保持运行状态。

后台进程

包含目前对用户不可见的Activity的进程(已调用Activity的onStop()方法)。这些进程对用户体验没有直接影响,系统可能随时终止它们,以回收内存供前台进程、可见进程或服务进程使用。通常会有很多后台进程在运行,因此它们会保存在LRU(最近最少使用)列表中,以确保包含用户最近查看的Activity的进程最后一个被终止。如果某个Activity正确实现了生命周期方法,并保存了其当前状态,则终止其进程不会对用户体验产生明显影响,因为当用户导航回该Activity时,Activity会恢复其所有可见状态。有关保存和恢复状态、或者异常杀死恢复可以参考前两篇文章。

空进程

不含任何活动应用组件的进程。保留这种进程的的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间,这就是所谓热启动。为了使系统资源在进程缓存和底层内核缓存之间保持平衡,系统往往会终止这些进程。

根据进程中当前活动组件的重要程度,Android会将进程评定为它可能达到的最高级别。例如,如果某进程托管着服务和可见Activity,则会将此进程评定为可见进程,而不是服务进程。此外,一个进程的级别可能会因其他进程对它的依赖而有所提高,即服务于另一进程的进程其级别永远不会低于其所服务的进程。例如,如果进程A中的内容提供程序为进程B中的客户端提供服务,或者如果进程A中的服务绑定到进程B中的组件,则进程A始终被视为至少与进程B同样重要。

通过Google文档,对不同进程的重要程度有了一个直观的认识,下面看一下量化到内存是什么样的呈现形式,这里针对不同的重要程度,做了进一步的细分,定义了重要级别ADJ,并将优先级存储到内核空间的进程结构体中去,供LowmemoryKiller参考:

以上介绍的目的只有一点:Android的应用进程是有优先级的,它的优先级跟当前是否存在展示界面,以及是否能被用户感知有关,越是被用户感知的的应用优先级越高(系统进程不考虑)。

Android应用的优先级是如何更新的

APP中很多操作都可能会影响进程列表的优先级,比如退到后台、移到前台等,都会潜在的影响进程的优先级,我们知道Lowmemorykiller是通过遍历内核的进程结构体队列,选择优先级低的杀死,那么APP操作是如何写入到内核空间的呢?Linxu有用户间跟内核空间的区分,无论是APP还是系统服务,都是运行在用户空间,严格说用户控件的操作是无法直接影响内核空间的,更不用说更改进程的优先级。

其实这里是通过了Linux中的一个proc文件体统,proc文件系统可以简单的看多是内核空间映射成用户可以操作的文件系统,当然不是所有进程都有权利操作,通过proc文件系统,用户空间的进程就能够修改内核空间的数据,比如修改进程的优先级,在Android家族,5.0之前的系统是AMS进程直接修改的,5.0之后,是修改优先级的操作被封装成了一个独立的服务-lmkd,lmkd服务位于用户空间,其作用层次同AMS、WMS类似,就是一个普通的系统服务。我们先看一下5.0之前的代码,这里仍然用4.3的源码看一下,模拟一个场景,APP只有一个Activity,我们主动finish掉这个Activity,APP就回到了后台,这里要记住,虽然没有可用的Activity,但是APP本身是没哟死掉的,这就是所谓的热启动,先看下大体的流程:

现在直接去AMS看源码:

ActivityManagerService

publicfinalbooleanfinishActivity(IBindertoken,intresultCode,IntentresultData){...synchronized(this){finallongorigId=Binder.clearCallingIdentity();booleanres=mMainStack.requestFinishActivityLocked(token,resultCode,resultData,"app-request",true);...}}

一开始的流程跟startActivity类似,首先是先暂停当前resume的Activity,其实也就是自己,

finalbooleanfinishActivityLocked(ActivityRecordr,intindex,intresultCode,IntentresultData,Stringreason,booleanimmediate,booleanoomAdj){...if(mPausingActivity==null){if(DEBUG_PAUSE)Slog.v(TAG,"Finishneedstopause:"+r);if(DEBUG_USER_LEAVING)Slog.v(TAG,"finish()=pausewithuserLeaving=false");startPausingLocked(false,false);}...}

pause掉当前Activity之后,还需要唤醒上一个Activity,如果当前APP的Activity栈里应经空了,就回退到上一个应用或者桌面程序,唤醒流程就不在讲解了,因为在AMS恢复异常杀死APP的那篇已经说过,这里要说的是唤醒之后对这个即将退回后台的APP的操作,这里注意与startActivity不同的地方,看下面代码:

ActivityStack

privatefinalvoid

1
查看完整版本: Android后台杀死系列LowMemo