前言
Android 8.0 / 8.1是目前 Android 最新的正式发行版智能手机操作系统,2017年3月21日 Google 为开发者推出了新的 Android O 首个开发者预览版,2017 Google I/O 开发者大会上发布了第二个Android O开发者预览。2017年8月22日,谷歌正式发布了Android 8.0的正式版,其正式名称为:Android Oreo(奥利奥)。2017年12月5日谷歌正式发布了Android 8.1的正式版。在最近的 Android 版本更新中,可以看得出来,Google 已经更加注重 Android 的流畅性和续航能力等性能,这个倾斜会让 Android 的用户体验变得越来越好。
据说谷歌重写的Android的底层源码,我在查看进程时就发现了一些显著的不同,当然我没有使用实机操作,不知道这个现象是否正常。以下为我日常使用的Sony Xperia Z5 Premium 搭载 Android 7.1.1 和AVD上的一个Android 8.0 的虚拟在adb shell中查看进程的不同之处。
可以看到AVD已经不再显示 root system 等USER的进程了,表头也发生了变化,sh进程NAME值为sh,而非7.0的/system/bin/sh。
Android系统架构
Android 操作系统是一个软件组件的栈,在架构图中它大致可以分为五个部分和四个主要层。
Linux内核
在所有层的最底下是 Linux - 包括大约115个补丁的 Linux 3.6。它提供了基本的系统功能,比如进程管理,内存管理,设备管理(如摄像头,键盘,显示器)。同时,内核处理所有 Linux 所擅长的工作,如网络和大量的设备驱动,从而避免兼容大量外围硬件接口带来的不便。
程序库
在 Linux 内核层的上面是一系列程序库的集合,包括开源的 Web 浏览器引擎 Webkit ,知名的 libc 库,用于仓库存储和应用数据共享的 SQLite 数据库,用于播放、录制音视频的库,用于网络安全的 SSL 库等。
Android程序库
这个类别包括了专门为 Android 开发的基于 Java 的程序库。这个类别程序库的示例包括应用程序框架库,如用户界面构建,图形绘制和数据库访问。一些 Android 开发者可用的 Android 核心程序库总结如下:
- android.app - 提供应用程序模型的访问,是所有 Android 应用程序的基石。
- android.content - 方便应用程序之间,应用程序组件之间的内容访问,发布,消息传递。
- android.database - 用于访问内容提供者发布的数据,包含 SQLite 数据库管理类。
- android.opengl - OpenGL ES 3D 图片渲染 API 的 Java 接口。
- android.os - 提供应用程序访问标注操作系统服务的能力,包括消息,系统服务和进程间通信。
- android.text - 在设备显示上渲染和操作文本。
- android.view - 应用程序用户界面的基础构建块。
- android.widget - 丰富的预置用户界面组件集合,包括按钮,标签,列表,布局管理,单选按钮等。
- android.webkit - 一系列类的集合,允许为应用程序提供内建的 Web 浏览能力。
看过了 Android 运行层内的基于 Java 的核心程序库,是时候关注一下 Android 软件栈中的基于 C/C++ 的程序库。
Android运行时
这是架构中的第三部分,自下而上的第二层。这个部分提供名为 Dalvik 虚拟机的关键组件,类似于 Java 虚拟机,但专门为 Android 设计和优化。
Dalvik 虚拟机使得可以在 Java 中使用 Linux 核心功能,如内存管理和多线程。Dalvik 虚拟机使得每一个 Android 应用程序运行在自己独立的虚拟机进程。
Android 运行时同时提供一系列核心的库来为 Android 应用程序开发者使用标准的 Java 语言来编写 Android 应用程序。
应用框架
应用框架层以 Java 类的形式为应用程序提供许多高级的服务。应用程序开发者被允许在应用中使用这些服务。
- 活动管理者 - 控制应用程序生命周期和活动栈的所有方面。
- 内容提供者 - 允许应用程序之间发布和分享数据。
- 资源管理器 - 提供对非代码嵌入资源的访问,如字符串,颜色设置和用户界面布局。
- 通知管理器 - 允许应用程序显示对话框或者通知给用户。
- 视图系统 - 一个可扩展的视图集合,用于创建应用程序用户界面。
应用程序
顶层中有所有的 Android 应用程序。你写的应用程序也将被安装在这层。这些应用程序包括通讯录,浏览器,游戏等。
Android进程
查看Android进程
Android是基于Linux的,但是没有terminal所以我们查看Android进程需要使用adb中的adb shell进入手机,再使用ps指令来查看,如前言所示。
Android进程
当某个应用组件启动且该应用没有运行其他任何组件时,Android 系统会使用单个执行线程为应用启动新的 Linux 进程。
默认情况下,同一应用的所有组件均在相同的进程中运行,且大多数应用都不会改变这一点。 但是,如果您发现需要控制某个组件所属的进程,则可在清单文件中执行此操作。
如果内存不足,而其他为用户提供更紧急服务的进程又需要内存时,Android 可能会决定在某一时刻关闭某一进程。在被终止进程中运行的应用组件也会随之销毁。 当这些组件需要再次运行时,系统将为它们重启进程。
决定终止哪个进程时,Android 系统将权衡它们对用户的相对重要程度。例如,相对于托管可见 Activity 的进程而言,它更有可能关闭托管屏幕上不再可见的 Activity 的进程。 因此,是否终止某个进程的决定取决于该进程中所运行组件的状态。
进程生命周期
Android 系统将尽量长时间地保持应用进程,但为了新建进程或运行更重要的进程,最终需要移除旧进程来回收内存。 为了确定保留或终止哪些进程,系统会根据进程中正在运行的组件以及这些组件的状态,将每个进程放入“重要性层次结构”中。 必要时,系统会首先消除重要性最低的进程,然后是重要性略逊的进程,依此类推,以回收系统资源。
Android系统共有五种等级的进程
重要性层次结构一共有 5 级。以下列表按照重要程度列出了各类进程(第一个进程最重要,将是最后一个被终止的进程):
- 前台进程
用户当前操作所必需的进程。如果一个进程满足以下任一条件,即视为前台进程:
- 托管用户正在交互的
Activity
(已调用Activity
的onResume()
方法)- 托管某个
Service
,后者绑定到用户正在交互的 Activity- 托管正在“前台”运行的
Service
(服务已调用startForeground()
)- 托管正执行一个生命周期回调的
Service
(onCreate()
、onStart()
或onDestroy()
)- 托管正执行其
onReceive()
方法的BroadcastReceiver
通常,在任意给定时间前台进程都为数不多。只有在内存不足以支持它们同时继续运行这一万不得已的情况下,系统才会终止它们。 此时,设备往往已达到内存分页状态,因此需要终止一些前台进程来确保用户界面正常响应。
- 可见进程
没有任何前台组件、但仍会影响用户在屏幕上所见内容的进程。 如果一个进程满足以下任一条件,即视为可见进程:
- 托管不在前台、但仍对用户可见的
Activity
(已调用其onPause()
方法)。例如,如果前台 Activity 启动了一个对话框,允许在其后显示上一 Activity,则有可能会发生这种情况。- 托管绑定到可见(或前台)Activity 的
Service
。可见进程被视为是极其重要的进程,除非为了维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程。
- 服务进程
正在运行已使用
startService()
方法启动的服务且不属于上述两个更高类别进程的进程。尽管服务进程与用户所见内容没有直接关联,但是它们通常在执行一些用户关心的操作(例如,在后台播放音乐或从网络下载数据)。因此,除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保持运行状态。- 后台进程
包含目前对用户不可见的 Activity 的进程(已调用 Activity 的
onStop()
方法)。这些进程对用户体验没有直接影响,系统可能随时终止它们,以回收内存供前台进程、可见进程或服务进程使用。 通常会有很多后台进程在运行,因此它们会保存在 LRU (最近最少使用)列表中,以确保包含用户最近查看的 Activity 的进程最后一个被终止。如果某个 Activity 正确实现了生命周期方法,并保存了其当前状态,则终止其进程不会对用户体验产生明显影响,因为当用户导航回该 Activity 时,Activity 会恢复其所有可见状态。 有关保存和恢复状态的信息,请参阅 Activity文档。- 空进程
不含任何活动应用组件的进程。保留这种进程的的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。 为使总体系统资源在进程缓存和底层内核缓存之间保持平衡,系统往往会终止这些进程。
进程优先级的定义
OOM adjustments与 Process Importance决定了进程在调用中的优先级。
ADJ值定义在frameworks\base\services\core\java\com\android\server\am\ProcessList.java中
// OOM adjustments for processes in various states: // Uninitialized value for any major or minor adj fields static final int INVALID_ADJ = -10000; // 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.) static final int UNKNOWN_ADJ = 1001; // This is a process only hosting activities that are not visible, // so it can be killed without any disruption. static final int CACHED_APP_MAX_ADJ = 906; static final int CACHED_APP_MIN_ADJ = 900; // 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 = 800; // 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 = 700; // 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 = 600; // 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 = 500; // 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 = 400; // 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 = 300; // 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 = 200; // 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 = 100; static final int VISIBLE_APP_LAYER_MAX = PERCEPTIBLE_APP_ADJ - 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 process that the system or a persistent process has bound to, // and indicated it is important. static final int PERSISTENT_SERVICE_ADJ = -700; // 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 = -800; // The system process runs at the default adjustment. static final int SYSTEM_ADJ = -900; // Special code for native processes that are not being managed by the system (so // don't have an oom adj assigned by the system). static final int NATIVE_ADJ = -1000;
ADJ值越小则优先度越高,例如INVALID和NATIVE的ADJ值在Android 8.0中为-10000和-1000,优先度最高,系统不会去关闭它。紧随其后的是SYSTEM,ADJ值为-900。
ADJ值 |
类型 |
-10000 |
INVALID 未定义 |
-1000 |
NATIVE 本机持有的特殊代码 |
-900 | SYSTEM |
-800 | PERSISTENT_PROC 系统持续进程(电话) |
-700 | PERSISTENT_SERVICE 系统服务进程 |
0 | FOREGROUND_APP 前台应用 |
100 | VISIBLE_APP 可见应用 |
200 | PERCEPTIBLE_APP 后台应用 |
300 | BACKUP_APP 备份进程 |
400 | HEAVY_WEIGHT_APP 重量级后台进程 |
500 | SERVICE 服务进程 |
600 | HOME_APP 主进程 |
700 | PREVIOUS_APP 上一个进程 |
800 | SERVICE_B 旧的服务进程B列表 |
900 | CACHED_APP_MIN |
906 | CACHED_APP_MAX |
1001 | UNKNOWN |
99(PERCEPTIBLE_APP_ADJ - VISIBLE_APP_ADJ - 1) |
VISIBLE_APP_LAYER_MAX |
Android 进程级别 和 oom_adj对应关系
一 : 前台进程 (Active Process): oom_adj为0。
前台进程包括 : 1 : 活动 正在前台接收用户输入
2:活动、服务与广播接收器正在执行一个onReceive事件的处理函数
3: 服务正在运行 onStart、onCreate或onDestroy事件处理函数。
二 : 已启动服务的进程(Started Service Process) :oom_adj值为0,这类进程包含一个已启动的服务。 服务并不直接与用户输入交互,因此服务的优先级
低于可见活动的优先级,但是,已启动服务的进程任被认为是前台进程,只有在活动以及可见活动需要资源时,已启动服务的进程才会被杀死。
三 :可见进程 (Visible Process): oom_adj 为 100。活动是可见的,但并不在前台,或者不响应用户的输入。例如,活动被非全屏或者透明的活动所遮挡。
//以下为旧版本
四 :后台进程 (Backgroud Process): oom_adj 值为 2,这类进程不包含任何可见的活动与启动的服务。通常大量后台进程存在时,系统会采用(last-seen-first-kill)后见先杀的方式,释放资源为前台进程使用。
五 :主界面 (home process): oom_adj 为 4
六 :隐藏进程 (hidden process): oom_adj为 7
七 :内容提供者 (content provider):oom_adj 为 14
八 :空进程 (Empty process):oom_adj为 15
Process Importance定义在frameworks/base/core/java/android/app/ActivityManager.java类中:
/** * Constant for {@link #importance}: This process is running the * foreground UI; that is, it is the thing currently at the top of the screen * that the user is interacting with. */ public static final int IMPORTANCE_FOREGROUND = 100; /** * Constant for {@link #importance}: This process is running a foreground * service, for example to perform music playback even while the user is * not immediately in the app. This generally indicates that the process * is doing something the user actively cares about. */ public static final int IMPORTANCE_FOREGROUND_SERVICE = 125; /** * Constant for {@link #importance}: This process is running the foreground * UI, but the device is asleep so it is not visible to the user. This means * the user is not really aware of the process, because they can not see or * interact with it, but it is quite important because it what they expect to * return to once unlocking the device. */ public static final int IMPORTANCE_TOP_SLEEPING = 150; /** * Constant for {@link #importance}: This process is running something * that is actively visible to the user, though not in the immediate * foreground. This may be running a window that is behind the current * foreground (so paused and with its state saved, not interacting with * the user, but visible to them to some degree); it may also be running * other services under the system's control that it inconsiders important. */ public static final int IMPORTANCE_VISIBLE = 200; /** * Constant for {@link #importance}: {@link #IMPORTANCE_PERCEPTIBLE} had this wrong value * before {@link Build.VERSION_CODES#O}. Since the {@link Build.VERSION_CODES#O} SDK, * the value of {@link #IMPORTANCE_PERCEPTIBLE} has been fixed. * *The system will return this value instead of {
@link #IMPORTANCE_PERCEPTIBLE} * on Android versions below {@link Build.VERSION_CODES#O}. * *On Android version {
@link Build.VERSION_CODES#O} and later, this value will still be * returned for apps with the target API level below {@link Build.VERSION_CODES#O}. * For apps targeting version {@link Build.VERSION_CODES#O} and later, * the correct value {@link #IMPORTANCE_PERCEPTIBLE} will be returned. */ public static final int IMPORTANCE_PERCEPTIBLE_PRE_26 = 130; /** * Constant for {@link #importance}: This process is not something the user * is directly aware of, but is otherwise perceptible to them to some degree. */ public static final int IMPORTANCE_PERCEPTIBLE = 230; /** * Constant for {@link #importance}: {@link #IMPORTANCE_CANT_SAVE_STATE} had * this wrong value * before {@link Build.VERSION_CODES#O}. Since the {@link Build.VERSION_CODES#O} SDK, * the value of {@link #IMPORTANCE_CANT_SAVE_STATE} has been fixed. * *The system will return this value instead of {
@link #IMPORTANCE_CANT_SAVE_STATE} * on Android versions below {@link Build.VERSION_CODES#O}. * *On Android version {
@link Build.VERSION_CODES#O} after, this value will still be * returned for apps with the target API level below {@link Build.VERSION_CODES#O}. * For apps targeting version {@link Build.VERSION_CODES#O} and later, * the correct value {@link #IMPORTANCE_CANT_SAVE_STATE} will be returned. * * @hide */ public static final int IMPORTANCE_CANT_SAVE_STATE_PRE_26 = 170; /** * 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= 270; /** * Constant for {@link #importance}: This process is contains services * that should remain running. These are background services apps have * started, not something the user is aware of, so they may be killed by * the system relatively freely (though it is generally desired that they * stay running as long as they want to). */ public static final int IMPORTANCE_SERVICE = 300; /** * Constant for {@link #importance}: This process process contains * cached code that is expendable, not actively running any app components * we care about. */ public static final int IMPORTANCE_CACHED = 400; /** * @deprecated Renamed to {@link #IMPORTANCE_CACHED}. */ public static final int IMPORTANCE_BACKGROUND = IMPORTANCE_CACHED; /** * Constant for {@link #importance}: This process is empty of any * actively running code. * @deprecated This value is no longer reported, use {@link #IMPORTANCE_CACHED} instead. */ @Deprecated public static final int IMPORTANCE_EMPTY = 500; /** * Constant for {@link #importance}: This process does not exist. */ public static final int IMPORTANCE_GONE = 1000;
IMPORTANCE值也是越小优先度越高。
IMPORTANCE_FOREGROUND 意味着这个进程正在运行前台UI,也就是说,它是当前在屏幕顶部的东西,用户正在进行交互的而进程,即之前提到的前台进程。
IMPORTANCE_FOREGROUND_SERVICE前台服务,即使用户不是在应用中时也执行音乐播放,这一般表示该进程正在做用户积极关心的事情。
在这里我们发现了一个Android 8.0不同以往的地方新增了IMPORTANCE_CACHED = 400的定义,而后台进程IMPORTANCE_BACKGROUND值与其相同。
IMPORTANCE值 | 属性 |
100 | FOREGROUND |
125 | FOREGROUND_SERVICE |
130 | PERCEPTIBLE_PRE_26 |
170 | CANT_SAVE_STATE_PRE_26 |
150 | TOP_SLEEPING |
200 | VISIBLE |
230 | PERCEPTIBLE |
270 | CANT_SAVE_STATE |
300 | SERVICE |
400 | CACHED & BACKGROUND |
500 | EMPTY |
1000 | GONE |
进程回收机制
这里参考自穿裤衩闯天下 的文章《Android 守护进程的实现方式》
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
值就会被杀掉。
下边是表示Process State
(即老版本里的OOM_ADJ
)数值对照表,数值越大,重要性越低,在新版SDK中已经在android
层去除了小于0的进程状态
// Path:SDK/sources/android-25/android/app/ActivityManager#RunningAppProcessInfo.java // 进程不存在。 public static final int PROCESS_STATE_NONEXISTENT = -1; // 进程是一个持久的系统进程,一般指当前 UI 进程 public static final int PROCESS_STATE_PERSISTENT = 0; // 进程是一个持久的系统进程,正在做和 UI 相关的操作,但不直接显示 public static final int PROCESS_STATE_PERSISTENT_UI = 1; // 进程正在托管当前的顶级活动。请注意,这涵盖了用户可见的所有活动。 public static final int PROCESS_STATE_TOP = 2; // 进程由于系统绑定而托管前台服务。 public static final int PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 3; // 进程正在托管前台服务。 public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4; // 与{@link #PROCESS_STATE_TOP}相同,但设备处于睡眠状态。 public static final int PROCESS_STATE_TOP_SLEEPING = 5; // 进程对用户很重要,是他们知道的东西 public static final int PROCESS_STATE_IMPORTANT_FOREGROUND = 6; // 进程对用户很重要,但不是他们知道的 public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 7; // 进程在后台运行备份/恢复操作 public static final int PROCESS_STATE_BACKUP = 8; // 进程在后台,但我们不能恢复它的状态,所以我们想尽量避免杀死它,不然这个而进程就丢了 public static final int PROCESS_STATE_HEAVY_WEIGHT = 9; // 进程在后台运行一个服务,与oom_adj不同,此级别用于正常运行在后台状态和执行操作状态。 public static final int PROCESS_STATE_SERVICE = 10; // 进程在后台运行一个接收器,注意,从oom_adj接收器的角度来看,在较高的前台级运行,但是对于我们的优先级,这不是必需的,并且将它们置于服务之下意味着当它们接收广播时,一些进程状态中的更少的改变。 public static final int PROCESS_STATE_RECEIVER = 11; // 进程在后台,但主持家庭活动 public static final int PROCESS_STATE_HOME = 12; // 进程在后台,但托管最后显示的活动 public static final int PROCESS_STATE_LAST_ACTIVITY = 13; // 进程正在缓存以供以后使用,并包含活动 public static final int PROCESS_STATE_CACHED_ACTIVITY = 14; // 进程正在缓存供以后使用,并且是包含活动的另一个缓存进程的客户端 public static final int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 15; // 进程正在缓存以供以后使用,并且为空 public static final int PROCESS_STATE_CACHED_EMPTY = 16;
在Android 8.0 中是这样的,增加了17、18两个值四个STATE:PROCESS_STATE_CACHED_EMPTY、PROCESS_STATE_NONEXISTENT、MIN_PROCESS_STATE、MAX_PROCESS_STATE。
/** @hide Not a real process state. */ public static final int PROCESS_STATE_UNKNOWN = -1; /** @hide Process is a persistent system process. */ public static final int PROCESS_STATE_PERSISTENT = 0; /** @hide Process is a persistent system process and is doing UI. */ public static final int PROCESS_STATE_PERSISTENT_UI = 1; /** @hide Process is hosting the current top activities. Note that this covers * all activities that are visible to the user. */ public static final int PROCESS_STATE_TOP = 2; /** @hide Process is hosting a foreground service due to a system binding. */ public static final int PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 3; /** @hide Process is hosting a foreground service. */ public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4; /** @hide Same as {@link #PROCESS_STATE_TOP} but while device is sleeping. */ public static final int PROCESS_STATE_TOP_SLEEPING = 5; /** @hide Process is important to the user, and something they are aware of. */ public static final int PROCESS_STATE_IMPORTANT_FOREGROUND = 6; /** @hide Process is important to the user, but not something they are aware of. */ public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 7; /** @hide Process is in the background transient so we will try to keep running. */ public static final int PROCESS_STATE_TRANSIENT_BACKGROUND = 8; /** @hide Process is in the background running a backup/restore operation. */ public static final int PROCESS_STATE_BACKUP = 9; /** @hide Process is in the background, but it can't restore its state so we want * to try to avoid killing it. */ public static final int PROCESS_STATE_HEAVY_WEIGHT = 10; /** @hide Process is in the background running a service. Unlike oom_adj, this level * is used for both the normal running in background state and the executing * operations state. */ public static final int PROCESS_STATE_SERVICE = 11; /** @hide Process is in the background running a receiver. Note that from the * perspective of oom_adj receivers run at a higher foreground level, but for our * prioritization here that is not necessary and putting them below services means * many fewer changes in some process states as they receive broadcasts. */ public static final int PROCESS_STATE_RECEIVER = 12; /** @hide Process is in the background but hosts the home activity. */ public static final int PROCESS_STATE_HOME = 13; /** @hide Process is in the background but hosts the last shown activity. */ public static final int PROCESS_STATE_LAST_ACTIVITY = 14; /** @hide Process is being cached for later use and contains activities. */ public static final int PROCESS_STATE_CACHED_ACTIVITY = 15; /** @hide Process is being cached for later use and is a client of another cached * process that contains activities. */ public static final int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 16; /** @hide Process is being cached for later use and is empty. */ public static final int PROCESS_STATE_CACHED_EMPTY = 17; /** @hide Process does not exist. */ public static final int PROCESS_STATE_NONEXISTENT = 18; /** @hide The lowest process state number */ public static final int MIN_PROCESS_STATE = PROCESS_STATE_PERSISTENT; /** @hide The highest process state number */ public static final int MAX_PROCESS_STATE = PROCESS_STATE_NONEXISTENT;
Process State
(即老版本的OOM_ADJ
)与Process Importance
对应关系,这个方法也是在ActivityManager.java
类中,有了这个关系,就知道可以知道我们的应用处于哪个级别,对于我们后边优化有个很好地参考
/** * Path:SDK/sources/android-25/android/app/ActivityManager#RunningAppProcessInfo.java * 这里在Android8.0中没有发生变化 * 通过这个方法,将Linux底层的 OOM_ADJ级别码和 android 层面的进程重要程度联系了起来 */ public static int procStateToImportance(int procState) { if (procState == PROCESS_STATE_NONEXISTENT) { return IMPORTANCE_GONE; } else if (procState >= PROCESS_STATE_HOME) { return IMPORTANCE_BACKGROUND; } else if (procState >= PROCESS_STATE_SERVICE) { return IMPORTANCE_SERVICE; } else if (procState > PROCESS_STATE_HEAVY_WEIGHT) { return IMPORTANCE_CANT_SAVE_STATE; } else if (procState >= PROCESS_STATE_IMPORTANT_BACKGROUND) { return IMPORTANCE_PERCEPTIBLE; } else if (procState >= PROCESS_STATE_IMPORTANT_FOREGROUND) { return IMPORTANCE_VISIBLE; } else if (procState >= PROCESS_STATE_TOP_SLEEPING) { return IMPORTANCE_TOP_SLEEPING; } else if (procState >= PROCESS_STATE_FOREGROUND_SERVICE) { return IMPORTANCE_FOREGROUND_SERVICE; } else { return IMPORTANCE_FOREGROUND; } }
一般情况下,设备端进程被干掉有一下几种情况
进程结束场景 | 结束方式 | 影响范围 |
---|---|---|
Android 系统自身内存回收机制 | Low Memory Killer | Process State 数值从大到小 |
第三方管理程序清理进程 无 Root 权限 | killBackgroundProcess | Process State 数值大于6进程 |
第三方管理程序清理进程 有 Root 权限 | force-stop or Kill | 除当前前台进程外所有非系统进程 |
Rom 清除进程(用户手动清理) | force-stop or Kill | 所有非系统进程 |
用户手动强制结束 | force-stop | 第三方应用以及非 System 进程 |
Android进程状态转换
这里参考了一篇CSDN的文章
Android应用进程启动
当我们在Launcher桌面上点击一个应用的图标时,当然Launcher启动之后,已经把桌面上每个图标对应的信息都封装好了,用户点击之后,Launcher进程就会通过Binder进程间通信机制调用startActivity的方式去打开目标进程的入口Activity,指令传达到ActivityManagerService当中时,AMS会去检测当有的应用进程是否已经启动,如果没有启动,那么就会先将当前的目标进程启动起来,启动目标进程是通过调用startProcessLocked方法来完成的。
方法的实现在ActivityManagerService类中,目录路径为frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java,startProcessLocked方法的源码如下:
private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr) { startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */, null /* entryPoint */, null /* entryPointArgs */); }
该方法就是直接调用另一个重载方法来实现在,该重载方法的源码如下:
private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) { long startTime = SystemClock.elapsedRealtime(); if (app.pid > 0 && app.pid != MY_PID) { checkTime(startTime, "startProcess: removing from pids map"); synchronized (mPidsSelfLocked) { mPidsSelfLocked.remove(app.pid); mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); } checkTime(startTime, "startProcess: done removing from pids map"); app.setPid(0); } if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG_PROCESSES, "startProcessLocked removing on hold: " + app); mProcessesOnHold.remove(app); checkTime(startTime, "startProcess: starting to update cpu stats"); updateCpuStats(); checkTime(startTime, "startProcess: done updating cpu stats"); try { try { final int userId = UserHandle.getUserId(app.uid); AppGlobals.getPackageManager().checkPackageStartable(app.info.packageName, userId); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } int uid = app.uid; int[] gids = null; int mountExternal = Zygote.MOUNT_EXTERNAL_NONE; if (!app.isolated) { int[] permGids = null; try { checkTime(startTime, "startProcess: getting gids from package manager"); final IPackageManager pm = AppGlobals.getPackageManager(); permGids = pm.getPackageGids(app.info.packageName, MATCH_DEBUG_TRIAGED_MISSING, app.userId); StorageManagerInternal storageManagerInternal = LocalServices.getService( StorageManagerInternal.class); mountExternal = storageManagerInternal.getExternalStorageMountMode(uid, app.info.packageName); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } /* * Add shared application and profile GIDs so applications can share some * resources like shared libraries and access user-wide resources */ if (ArrayUtils.isEmpty(permGids)) { gids = new int[3]; } else { gids = new int[permGids.length + 3]; System.arraycopy(permGids, 0, gids, 3, permGids.length); } gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid)); gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid)); gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid)); } checkTime(startTime, "startProcess: building args"); if (mFactoryTest != FactoryTest.FACTORY_TEST_OFF) { if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL && mTopComponent != null && app.processName.equals(mTopComponent.getPackageName())) { uid = 0; } if (mFactoryTest == FactoryTest.FACTORY_TEST_HIGH_LEVEL && (app.info.flags & ApplicationInfo.FLAG_FACTORY_TEST) != 0) { uid = 0; } } int debugFlags = 0; if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { debugFlags |= Zygote.DEBUG_ENABLE_JDWP; debugFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE; // Also turn on CheckJNI for debuggable apps. It's quite // awkward to turn on otherwise. debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI; } // Run the app in safe mode if its manifest requests so or the // system is booted in safe mode. if ((app.info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0 || mSafeMode == true) { debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE; } if ("1".equals(SystemProperties.get("debug.checkjni"))) { debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI; } String genDebugInfoProperty = SystemProperties.get("debug.generate-debug-info"); if ("true".equals(genDebugInfoProperty)) { debugFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO; } if ("1".equals(SystemProperties.get("debug.jni.logging"))) { debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING; } if ("1".equals(SystemProperties.get("debug.assert"))) { debugFlags |= Zygote.DEBUG_ENABLE_ASSERT; } if (mNativeDebuggingApp != null && mNativeDebuggingApp.equals(app.processName)) { // Enable all debug flags required by the native debugger. debugFlags |= Zygote.DEBUG_ALWAYS_JIT; // Don't interpret anything debugFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO; // Generate debug info debugFlags |= Zygote.DEBUG_NATIVE_DEBUGGABLE; // Disbale optimizations mNativeDebuggingApp = null; } String invokeWith = null; if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { // Debuggable apps may include a wrapper script with their library directory. String wrapperFileName = app.info.nativeLibraryDir + "/wrap.sh"; StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); try { if (new File(wrapperFileName).exists()) { invokeWith = "/system/bin/logwrapper " + wrapperFileName; } } finally { StrictMode.setThreadPolicy(oldPolicy); } } String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi; if (requiredAbi == null) { requiredAbi = Build.SUPPORTED_ABIS[0]; } String instructionSet = null; if (app.info.primaryCpuAbi != null) { instructionSet = VMRuntime.getInstructionSet(app.info.primaryCpuAbi); } app.gids = gids; app.requiredAbi = requiredAbi; app.instructionSet = instructionSet; // the per-user SELinux context must be set if (TextUtils.isEmpty(app.info.seInfoUser)) { Slog.wtf(TAG, "SELinux tag not defined", new IllegalStateException("SELinux tag not defined for " + app.info.packageName + " (uid " + app.uid + ")")); } final String seInfo = app.info.seInfo + (TextUtils.isEmpty(app.info.seInfoUser) ? "" : app.info.seInfoUser); // Start the process. It will either succeed and return a result containing // the PID of the new process, or else throw a RuntimeException. boolean isActivityProcess = (entryPoint == null); if (entryPoint == null) entryPoint = "android.app.ActivityThread"; Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " + app.processName); checkTime(startTime, "startProcess: asking zygote to start proc"); ProcessStartResult startResult; if (hostingType.equals("webview_service")) { startResult = startWebView(entryPoint, app.processName, uid, uid, gids, debugFlags, mountExternal, app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet, app.info.dataDir, null, entryPointArgs); } else { startResult = Process.start(entryPoint, app.processName, uid, uid, gids, debugFlags, mountExternal, app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet, app.info.dataDir, invokeWith, entryPointArgs); } checkTime(startTime, "startProcess: returned from zygote!"); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); mBatteryStatsService.noteProcessStart(app.processName, app.info.uid); checkTime(startTime, "startProcess: done updating battery stats"); EventLog.writeEvent(EventLogTags.AM_PROC_START, UserHandle.getUserId(uid), startResult.pid, uid, app.processName, hostingType, hostingNameStr != null ? hostingNameStr : ""); try { AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.processName, app.uid, seInfo, app.info.sourceDir, startResult.pid); } catch (RemoteException ex) { // Ignore } if (app.persistent) { Watchdog.getInstance().processStarted(app.processName, startResult.pid); } checkTime(startTime, "startProcess: building log message"); StringBuilder buf = mStringBuilder; buf.setLength(0); buf.append("Start proc "); buf.append(startResult.pid); buf.append(':'); buf.append(app.processName); buf.append('/'); UserHandle.formatUid(buf, uid); if (!isActivityProcess) { buf.append(" ["); buf.append(entryPoint); buf.append("]"); } buf.append(" for "); buf.append(hostingType); if (hostingNameStr != null) { buf.append(" "); buf.append(hostingNameStr); } Slog.i(TAG, buf.toString()); app.setPid(startResult.pid); app.usingWrapper = startResult.usingWrapper; app.removed = false; app.killed = false; app.killedByAm = false; checkTime(startTime, "startProcess: starting to update pids map"); ProcessRecord oldApp; synchronized (mPidsSelfLocked) { oldApp = mPidsSelfLocked.get(startResult.pid); } // If there is already an app occupying that pid that hasn't been cleaned up if (oldApp != null && !app.isolated) { // Clean up anything relating to this pid first Slog.w(TAG, "Reusing pid " + startResult.pid + " while app is still mapped to it"); cleanUpApplicationRecordLocked(oldApp, false, false, -1, true /*replacingPid*/); } synchronized (mPidsSelfLocked) { this.mPidsSelfLocked.put(startResult.pid, app); if (isActivityProcess) { Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG); msg.obj = app; mHandler.sendMessageDelayed(msg, startResult.usingWrapper ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT); } } checkTime(startTime, "startProcess: done updating pids map"); } catch (RuntimeException e) { Slog.e(TAG, "Failure starting process " + app.processName, e); // Something went very wrong while trying to start this process; one // common case is when the package is frozen due to an active // upgrade. To recover, clean up any active bookkeeping related to // starting this process. (We already invoked this method once when // the package was initially frozen through KILL_APPLICATION_MSG, so // it doesn't hurt to use it again.) forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid), false, false, true, false, false, UserHandle.getUserId(app.userId), "start failure"); } }
该方法中int uid = app.uid可以获取到目标进程的uid,uid是标识目标进程身份的一个整数,它最原始的分配是在应用进程安装时,由PackageManagerService分配好
关于Android进程模型
在安装Android应用程序的时候,Android会为每个程序分配一个Linux用户id,并设置相应的权限,这样其它应用程序就不能访问此应用程序所拥有的数据和资源了。
默认情况下,每个apk运行在它自己的Linux进程中。当需要执行应用程序中的代码时,Android会启动一个jvm,即一个新的进程来执行,因此不同的apk运行在相互隔离的环境中。
同时,开发者可以给两个应用程序分配相同的linux用户id,这样他们就能访问对方所拥有的资源。为了保留系统资源,拥有相同用户id的应用程序可以运行在同一个进程中,共享同一个jvm。
课堂演示
众所周知,Linux中的所有进程都是有init进程创建并运行的。首先Linux内核启动,然后在用户空间中启动init进程,再启动其他系统进程。在系统启动完成完成后,init将变为守护进程监视系统其他进程。Android是基于Linux的操作系统,所以init也是Android系统中用户空间的第一个进程,它的进程号是1。下面先简单的看一下init进程的启动过程。
Android 的线程调度
Android 进程生命周期与ADJ
Android 开发者应该都知道在系统中进程重要性的划分:
- 前台进程(Foreground process)
- 可见进程(Visible process)
- 服务进程(Service process)
- 后台进程(Background process)
- 空进程(Empty process)
相信大家都很清楚,这里就不做过多的介绍了,不过对于进程重要性是通过哪些操作发生变更的,以及和我们前面讲的 Linux 进程分组又是怎么关联和映射上的,是下面要讲述的重点。
Android 进程优先级的相关概念
oom_score_adj
级别
对于每一个运行中的进程,Linux 内核都通过 proc 文件系统暴露 /proc/[pid]/oom_score_adj
这样一个文件来允许其他程序修改指定进程的优先级,这个文件允许的值的范围是:-1000 ~ +1001之间。值越小,表示进程越重要。当内存非常紧张时,系统便会遍历所有进程,以确定哪个进程需要被杀死以回收内存,此时便会读取 oom_score_adj
这个文件的值。
PS:在Linux 2.6.36之前的版本中,Linux 提供调整优先级的文件是/proc/[pid]/oom_adj
。这个文件允许的值的范围是-17 ~ +15之间。数值越小表示进程越重要。 这个文件在新版的 Linux 中已经废弃。但你仍然可以使用这个文件,当你修改这个文件的时候,内核会直接进行换算,将结果反映到oom_score_adj
这个文件上。
Android早期版本的实现中也是依赖oom_adj
这个文件。但是在新版本中,已经切换到使用oom_score_adj
这个文件。
为了便于管理,ProcessList.java中预定义了 oom_score_adj
的可能取值,这里的预定义值也是对应用进程的一种分类。
Lowmemorykiller 根据当前可用内存情况来进行进程释放,总设计了6个级别,即上表中“解释列”加粗的行,即 Lowmemorykiller 的杀进程的6档,如下:
CACHED_APP_MAX_ADJ
CACHED_APP_MIN_ADJ
BACKUP_APP_ADJ
PERCEPTIBLE_APP_ADJ
VISIBLE_APP_ADJ
FOREGROUND_APP_ADJ
系统内存从很宽裕到不足,Lowmemorykiller 也会相应地从 CACHED_APP_MAX_ADJ
(第1档)开始杀进程,如果内存还不足,那么会杀 CACHED_APP_MIN_ADJ
(第2档),不断深入,直到满足内存阈值条件。
ProcessRecord中下面这些属性反应了 oom_score_adj
的值:
int maxAdj; // Maximum OOM adjustment for this process
int curRawAdj; // Current OOM unlimited adjustment for this process int setRawAdj; // Last set OOM unlimited adjustment for this process int curAdj; // Current OOM adjustment for this process int setAdj; // Last set OOM adjustment for this process int verifiedAdj; // The last adjustment that was verified as actually being set
Process State
对应的在 ActivityManager 重定义了 process_state 级别的划分,Android 系统会在修改进程状态的同时更新 oom_score_adj
的分级:
在 ProcessRecord 中,记录了和进程状态相关的属性:
int curProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state int repProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state int setProcState = PROCESS_STATE_NONEXISTENT; // Last set process state in process tracker int pssProcState = PROCESS_STATE_NONEXISTENT; // Currently requesting pss for
Schedule Group
对应到底层进程分组,除了上面提到的 Process.java
定义的不同线程组的定义,同时还为 Activity manager
定义了一套类似的调度分组,和之前的线程分组定义也存在对应关系:
// Activity manager's version of Process.THREAD_GROUP_BG_NONINTERACTIVE
static final int SCHED_GROUP_BACKGROUND = 0; // Activity manager's version of Process.THREAD_GROUP_DEFAULT static final int SCHED_GROUP_DEFAULT = 1; // Activity manager's version of Process.THREAD_GROUP_TOP_APP static final int SCHED_GROUP_TOP_APP = 2; // Activity manager's version of Process.THREAD_GROUP_TOP_APP // Disambiguate between actual top app and processes bound to the top app static final int SCHED_GROUP_TOP_APP_BOUND = 3;
在 ProcessRecord 中,也记录了和调度组相关的属性:
int curSchedGroup; // Currently desired scheduling class
int setSchedGroup; // Last set to background scheduling class
Android 进程优先级的变化
我们知道影响 Android 应用进程优先级变化的是根据 Android
应用组件的生命周期变化相关。Android进程调度之adj算法 里面罗列了所有会触发进程状态发生变化的事件,主要包括:
Actvity
- ActivityStackSupervisor.realStartActivityLocked: 启动Activity
- ActivityStack.resumeTopActivityInnerLocked: 恢复栈顶Activity
- ActivityStack.finishCurrentActivityLocked: 结束当前Activity
- ActivityStack.destroyActivityLocked: 摧毁当前Activity
Service
位于ActiveServices.java
- realStartServiceLocked: 启动服务
- bindServiceLocked: 绑定服务(只更新当前app)
- unbindServiceLocked: 解绑服务 (只更新当前app)
- bringDownServiceLocked: 结束服务 (只更新当前app)
- sendServiceArgsLocked: 在bringup或则cleanup服务过程调用 (只更新当前app)
broadcast
- BQ.processNextBroadcast: 处理下一个广播
- BQ.processCurBroadcastLocked: 处理当前广播
- BQ.deliverToRegisteredReceiverLocked: 分发已注册的广播 (只更新当前app)
ContentProvider
- AMS.removeContentProvider: 移除provider
- AMS.publishContentProviders: 发布provider (只更新当前app)
- AMS.getContentProviderImpl: 获取provider (只更新当前app)
Process
位于 ActivityManagerService.java
- setSystemProcess: 创建并设置系统进程
- addAppLocked: 创建persistent进程
- attachApplicationLocked: 进程创建后attach到system_server的过程;
- trimApplications: 清除没有使用app
- appDiedLocked: 进程死亡
- killAllBackgroundProcesses: 杀死所有后台进程.即(ADJ>9或removed=true的普通进程)
- killPackageProcessesLocked: 以包名的形式 杀掉相关进程;
这些事件都会直接或间接调用到 ActivityManagerService.java
中的 updateOomAdjLocked
方法来更新进程的优先级,updateOomAdjLocked
先通过 computeOomAdjLocked
方法负责计算进程的优先级,再通过调用 applyOomAdjLocked
应用进程的优先级。
computeOomAdjLocked
computeOomAdjLocked
方法负责计算进程的优先级,总计约700行,执行流程比较清晰,步骤如下,由于代码有点多这里就不贴了,想仔细研究的可以比着系统源码看: