💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
前面介绍了Linux OS中进程管理(包括调度和OOM控制)方面的API,但AMS是如何利用它们的呢?这就涉及AMS中的进程管理规则了。这里简单介绍相关规则。 Android将应用进程分为五大类,分别为Forground类、Visible类、Service类、Background类及Empty类。这五大类的划分各有规则。 1. 进程分类 (1) Forground类 该类中的进程重要性最高,属于该类的进程包括下面几种情况: - 含一个前端Activity(即onResume函数被调用过了,或者说当前正在显示的那个Activity)。 - 含一个Service,并且该Service和一个前端Activity绑定(例如Music应用包括一个前端界面和一个播放Service,当我们一边听歌一边操作Music界面时,该Service即和一个前端Activity绑定)。 - 含一个调用了startForground的Service,或者该进程的Service正在调用其生命周期的函数(onCreate、onStart或onDestroy)。 - 最后一种情况是,该进程中有BroadcastReceiver实例正在执行onReceive函数。 (2) Visible类 属于Visible类的进程中没有处于前端的组件,但是用户仍然能看到它们,例如位于一个对话框后的Activity界面。目前该类进程包括两种: - 该进程包含一个仅onPause被调用的Activity(即它还在前台,只不过部分界面被遮住)。 - 或者包含一个Service,并且该Service和一个Visible(或Forground)的Activity绑定(从字面意义上看,这种情况不太好和Forground进程中第二种情况区分)。 (3) Service类、Background类及Empty类 这三类进程都没有可见的部分,具体情况如下。 - Service进程:该类进程包含一个Service。此Service通过startService启动,并且不属于前面两类进程。这种进程一般在后台默默地干活,例如前面介绍的MediaScannerService。 - Background进程:该类进程包含当前不可见的Activity(即它们的onStop被调用过)。系统保存这些进程到一个LRU(最近最少使用)列表。当系统需要回收内存时,该列表中那些最近最少使用的进程将被杀死。 - Empty进程:这类进程中不包含任何组件。为什么会出现这种不包括任何组件的进程呢?其实很简单,假设该进程仅创建了一个Activity,它完成工作后主动调用finish函数销毁(destroy)自己,之后该进程就会成为Empty进程。系统保留Empty进程的原因是当又重新需要它们时(例如用户在别的进程中通过startActivity启动了它们),可以省去fork进程、创建Android运行环境等一系列漫长而艰苦的工作。 通过以上介绍可发现,当某个进程和前端显示有关系时,其重要性相对要高,这或许是体现Google重视用户体验的一个很直接的证据吧。 * * * * * **建议**:读者可阅读SDK/docs/guide/topics/fundamentals/processes-and-threads.html以获取更为详细的信息。 * * * * * 2. Process类API介绍 我们先来介绍Android平台中进程调度和OOM控制的API,它们统一被封装在Process.java中,其相关代码如下: **Process.java** ~~~ //设置线程的调度优先级,Linux kernel并不区分线程和进程,二者对应同一个数据结构Task public static final native void setThreadPriority(inttid, int priority) throws IllegalArgumentException, SecurityException; /* 设置线程的Group,实际上就是设置线程的调度策略,目前Android定义了三种Group。 由于缺乏相关资料,加之笔者感觉对应的注释也只可意会不可言传,故此处直接照搬了英文 注释,敬请读者谅解。 THREAD_GROUP_DEFAULT:Default thread group - gets a 'normal'share of the CPU THREAD_GROUP_BG_NONINTERACTIVE:Background non-interactive thread group. Allthreads in this group are scheduled with a reduced share of the CPU THREAD_GROUP_FG_BOOST:Foreground 'boost' thread group - Allthreads in this group are scheduled with an increasedshare of the CPU. 目前代码中还没有地方使用THREAD_GROUP_FG_BOOST这种Group */ public static final native void setThreadGroup(inttid, int group) throws IllegalArgumentException, SecurityException; //设置进程的调度策略,包括该进程的所有线程 public static final native voidsetProcessGroup(int pid, int group) throws IllegalArgumentException, SecurityException; //设置线程的调度优先级 public static final native voidsetThreadPriority(int priority) throws IllegalArgumentException, SecurityException; //调整进程的oom_adj值 public static final native boolean setOomAdj(intpid, int amt); ~~~ Process类还为不同调度优先级定义一些非常直观的名字以避免在代码中直接使用整型,例如为最低的调度优先级19定义了整型变量THREAD_PRIORITY_LOWEST。除此之外,Process还提供了fork子进程等相关的函数。 * * * * * **注意**:Process.java中的大多数函数是由JNI层实现的,其中Android在调度策略设置这一功能上还有一些特殊的地方,感兴趣的读者不妨阅读system/core/libcutils/sched_policy.c文件。 * * * * * 3. 关于ProcessList类和ProcessRecord类的介绍 (1) ProcessList类的介绍 ProcessList类有两个主要功能: - 定义一些成员变量,这些成员变量描述了不同状态下进程的oom_adj值。 - 在Android 4.0之后,LMK的配置参数由ProcessList综合考虑手机总内存大小和屏幕尺寸后再行设置(在Android 2.3中,LMK的配置参数在init.rc中由init进程设置,并且没有考虑屏幕尺寸的影响)。读者可自行阅读其updateOomLevels函数,此处不再赘述。 本节主要关注ProcessList对oom_adj的定义。虽然前面介绍时将Android进程分为五大类,但是在实际代码中的划分更为细致,考虑得更为周全。 **ProcessList.java** ~~~ class ProcessList { //当一个进程连续发生Crash的间隔小于60秒时,系统认为它是为Bad进程 staticfinal int MIN_CRASH_INTERVAL = 60*1000; //下面定义各种状态下进程的oom_adj //不可见进程的oom_adj,最大15,最小为7。杀死这些进程一般不会带来用户能够察觉的影响 staticfinal int HIDDEN_APP_MAX_ADJ = 15; staticint HIDDEN_APP_MIN_ADJ = 7; /* 位于B List中Service所在进程的oom_adj。什么样的Service会在BList中呢?其中的 解释只能用原文来表达” these are the old anddecrepit services that aren't as shiny andinteresting as the ones in the A list“ */ staticfinal int SERVICE_B_ADJ = 8; /* 前一个进程的oom_adj,例如从A进程的Activity跳转到位于B进程的Activity, B进程是当前进程,而A进程就是previous进程了。因为从当前进程A退回B进程是一个很简单 却很频繁的操作(例如按back键退回上一个Activity),所以previous进程的oom_adj 需要单独控制。这里不能简单按照五大类来划分previous进程,还需要综合考虑 */ staticfinal int PREVIOUS_APP_ADJ = 7; //Home进程的oom_adj为6,用户经常和Home进程交互,故它的oom_adj也需要单独控制 staticfinal int HOME_APP_ADJ = 6; //Service类中进程的oom_adj为5 staticfinal int SERVICE_ADJ = 5; //正在执行backup操作的进程,其oom_adj为4 staticfinal int BACKUP_APP_ADJ = 4; //heavyweight的概念前面曽提到过,该类进程的oom_adj为3 staticfinal int HEAVY_WEIGHT_APP_ADJ = 3; //Perceptible进程指那些当前并不在前端显示而用户能感觉到它在运行的进程,例如正在 //后台播放音乐的Music staticfinal int PERCEPTIBLE_APP_ADJ = 2; //Visible类进程,oom_adj为1 staticfinal int VISIBLE_APP_ADJ = 1; //Forground类进程,oom_adj为0 staticfinal int FOREGROUND_APP_ADJ = 0; //persistent类进程(即退出后,系统需要重新启动的重要进程),其oom_adj为-12 staticfinal int PERSISTENT_PROC_ADJ = -12; //核心服务所在进程,oom_adj为-12 staticfinal int CORE_SERVER_ADJ = -12; //系统进程,其oom_adj为-16 staticfinal int SYSTEM_ADJ = -16; //内存页大小定义为4KB staticfinal int PAGE_SIZE = 4*1024; //系统中能容纳的不可见进程数最少为2,最多为15。在Android 4.0系统中,可通过设置程序来 //调整 staticfinal int MIN_HIDDEN_APPS = 2; staticfinal int MAX_HIDDEN_APPS = 15; //下面两个参数用于调整进程的LRU权重。注意,它们的注释和具体代码中的用法不能统一起来 staticfinal long CONTENT_APP_IDLE_OFFSET = 15*1000; staticfinal long EMPTY_APP_IDLE_OFFSET = 120*1000; //LMK设置了6个oom_adj阈值 privatefinal int[] mOomAdj = new int[] { FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ, BACKUP_APP_ADJ, HIDDEN_APP_MIN_ADJ, EMPTY_APP_ADJ }; //用于内存较低机器(例如小于512MB)中LMK的内存阈值配置 privatefinal long[] mOomMinFreeLow = new long[] { 8192, 12288, 16384, 24576, 28672, 32768 }; //用于较高内存机器(例如大于1GB)的LMK内存阈值配置 privatefinal long[] mOomMinFreeHigh = new long[] { 32768, 40960, 49152, 57344, 65536, 81920 }; ~~~ 从以上代码中定义的各种ADJ值可知,AMS中的进程管理规则远比想象得要复杂(读者以后见识到具体的代码,更会有这样的体会)。 * * * * * **说明**:在ProcessList中定义的大部分变量在Android 2.3代码中定义于ActivityManagerService.java中,但这段代码的开发者仅把代码复制了过来,其中的注释并未随着系统升级而更新。 * * * * * (2) ProcessRecord中相关成员变量的介绍 ProcessRecord定义了较多成员变量用于进程管理。笔者不打算深究其中的细节。这里仅把其中的主要变量及一些注释列举出来。下文会分析到它们的作用。 **ProcessRecord.java** ~~~ //用于LRU列表控制 long lastActivityTime; // For managing the LRU list long lruWeight; // Weight for ordering in LRU list //和oom_adj有关 int maxAdj; // Maximum OOM adjustment for thisprocess int hiddenAdj; // If hidden, this is the adjustment touse int curRawAdj; // Current OOM unlimited adjustment forthis process int setRawAdj; // Last set OOM unlimited adjustment forthis process int curAdj; // Current OOM adjustment for thisprocess int setAdj; // Last set OOM adjustment for thisprocess //和调度优先级有关 int curSchedGroup; // Currently desired scheduling class int setSchedGroup; // Last set to background scheduling class //回收内存级别,见后文解释 int trimMemoryLevel; // Last selected memorytrimming level //判断该进程的状态,主要和其中运行的Activity,Service有关 boolean keeping; // Actively running code sodon't kill due to that? boolean setIsForeground; // Running foreground UI when last set? boolean foregroundServices; // Running anyservices that are foreground? boolean foregroundActivities; // Running anyactivities that are foreground? boolean systemNoUi; // This is a system process, but notcurrently showing UI. boolean hasShownUi; // Has UI been shown in this process since itwas started? boolean pendingUiClean; // Want to clean up resources from showingUI? boolean hasAboveClient; // Bound using BIND_ABOVE_CLIENT, so wantto be lower //是否处于系统BadProcess列表 boolean bad; // True if disabled in the badprocess list //描述该进程因为是否有太多后台组件而被杀死 boolean killedBackground; // True when proc has been killed due to toomany bg String waitingToKill; // Process is waiting to be killed whenin the bg; reason //序号,每次调节进程优先级或者LRU列表位置时,这些序号都会递增 int adjSeq; // Sequence id for identifyingoom_adj assignment cycles int lruSeq; // Sequence id for identifyingLRU update cycles ~~~ 上面注释中提到了LRU(最近最少使用)一词,它和AMS另外一个用于管理应用进程ProcessRecord的数据结构有关。 * * * * * **提示**:进程管理和调度一向比较复杂,从ProcessRecord定义的这些变量中可见一斑。需要提醒读者的是,对这部分功能的相关说明非常少,代码读起来会感觉比较晦涩。 * * * * *