Android——应用进程和应用生命周期

1. 概述

在大多数情况下,每个 Android 应用都在各自的 Linux 进程中运行。当需要运行应用的一些代码时,系统会为应用创建此进程,并使其保持运行,直到不再需要它且系统需要回收其内存以供其他应用使用。

应用进程的生命周期并不由应用本身直接控制,而是由系统综合多种因素来确定的,比如系统所知道的正在运行的应用部分、这些内容对用户的重要程度,以及系统中可用的总内存量。这是 Android 非常独特的一个基本功能。

一个不常见但很基础的Android特性是,一个应用进程的生命周期并不是由应用本身直接控制的。它是由系统根据正在运行的程序,对用户的重要程度以及所占用的内存,综合去管理的。

2. 应用进程

进程生命周期错误的一个常见示例是当 BroadcastReceiver 在其 BroadcastReceiver.onReceive() 方法中接收到一个 Intent 时,它会启动一个线程,然后从该函数返回。一旦返回,则系统会认为 BroadcastReceiver 不再处于活动状态,因此不再需要其托管进程(除非其中有其他应用组件处于活动状态)。
因此,系统可能会随时终止进程以回收内存,这样会终止在进程中运行的衍生线程。要解决这个问题,通常可以从 BroadcastReceiver 调度 JobService,这样系统就知道进程中还有处于活动状态的任务正在进行中。

为了确定在内存不足时应该终止哪些进程,Android 会根据每个进程中运行的组件以及这些组件的状态,将它们放入“重要性层次结构”。这些进程类型包括(按重要性排序):

2.1 前台进程:

前台进程:是用户目前执行操作所需的进程。在不同的情况下,进程可能会因为其所包含的各种应用组件而被视为前台进程。如果以下任一条件成立,则进程会被认为位于前台:

  • 它正在用户的互动屏幕上运行一个 Activity(其 onResume() 方法已被调用)。
  • 它有一个 BroadcastReceiver 目前正在运行(其 BroadcastReceiver.onReceive() 方法正在执行)。
  • 它有一个 Service 目前正在执行其某个回调(Service.onCreate()、Service.onStart() 或 Service.onDestroy())中的代码。

系统中只有少数此类进程,而且除非内存过低,导致连这些进程都无法继续运行,才会在最后一步终止这些进程。通常,此时设备已达到内存分页状态,因此必须执行此操作才能使用户界面保持响应。

2.2 可见进程:

可见进程:正在进行用户当前知晓的任务,因此终止该进程会对用户体验造成明显的负面影响。在以下条件下,进程将被视为可见:

  • 它正在运行的 Activity 在屏幕上对用户可见,但不在前台(其 onPause() 方法已被调用)。举例来说,如果前台 Activity 显示为一个对话框,而这个对话框允许在其后面看到上一个 Activity,则可能会出现这种情况。
  • 它有一个 Service 正在通过 Service.startForeground()(要求系统将该服务视为用户知晓或基本上对用户可见的服务)作为前台服务运行。
  • 系统正在使用其托管的服务实现用户知晓的特定功能,例如动态壁纸、输入法服务等。

相比前台进程,系统中运行的这些进程数量较不受限制,但仍相对受控。这些进程被认为非常重要,除非系统为了使所有前台进程保持运行而需要终止它们,否则不会这么做。

2.3 服务流程

服务流程包含一个已使用 startService() 方法启动的 Service。虽然用户无法直接看到这些进程,但它们通常正在执行用户关心的任务(例如后台网络数据上传或下载),因此系统会始终使此类进程保持运行,除非没有足够的内存来保留所有前台和可见进程。

已经运行了很长时间(例如 30 分钟或更长时间)的服务的重要性可能会降位,以使其进程降至下文所述的缓存 LRU 列表。这有助于避免超长时间运行的服务因内存泄露或其他问题占用大量内存,进而妨碍系统有效利用缓存进程。

2.4 缓存进程

缓存进程是目前不需要的进程,因此,如果其他地方需要内存,系统可以根据需要自由地终止该进程。在正常运行的系统中,这些是内存管理中涉及的唯一进程:运行良好的系统将始终有多个缓存进程可用(为了更高效地切换应用),并根据需要定期终止最早的进程。只有在非常危急(且具有不良影响)的情况下,系统中的所有缓存进程才会被终止,此时系统必须开始终止服务进程。

这些进程通常包含用户当前不可见的一个或多个 Activity 实例(onStop() 方法已被调用并返回)。只要它们正确实现其 Activity 生命周期,那么当系统终止此类流程时,就不会影响用户返回该应用时的体验,因为当关联的 Activity 在新的进程中重新创建时,它可以恢复之前保存的状态。

这些进程保存在伪 LRU 列表中,列表中的最后一个进程是为了回收内存而终止的第一个进程。此列表的确切排序政策是平台的实现细节,但它通常会先尝试保留更多有用的进程(比如托管用户的主屏幕应用、用户最后看到的 Activity 的进程等),再保留其他类型的进程。还可以针对终止进程应用其他政策:比如对允许的进程数量的硬限制,对进程可持续保持缓存状态的时间长短的限制等。

3. App启动流程

此处讨论的是第一次启动App。在讲解App启动流程的时候,有两点需要知晓:

  • 每一个App都运行在一个独立空间里,也可以称之为沙盒,这意味着它是在独立的线程中,拥有自己的虚拟机实例,被分配一个唯一的用户ID。
  • App由很多不同组建组成,这些组件还可以调用其他App的组建,并没有一个单独的类似于main函数的入口。

总体来说,App启动分为三个步骤,创建进程、绑定Application以及启动Activity。当用户点击Android桌面一个App图标,启动一个应用的时候,整个流程如下所示:

在这里插入图片描述

  • ①点击桌面App图标,Launcher进程采用Binder IPC向system_server进程发起startActivity请求;
  • ②system_server进程接收到请求后,向zygote进程发送创建进程的请求;
  • ③Zygote进程fork出新的子进程,即App进程;
  • ④App进程,通过Binder IPC向sytem_server进程发起attachApplication请求;
  • ⑤system_server进程在收到请求后,进行一系列准备工作后,再通过binder IPC向App进程发送scheduleLaunchActivity请求;
  • ⑥App进程的binder线程(ApplicationThread)在收到请求后,通过handler向主线程发送LAUNCH_ACTIVITY消息;
  • ⑦主线程在收到Message后,通过发射机制创建目标Activity,并回调Activity.onCreate()等方法。
  • ⑧到此,App便正式启动,开始进入Activity生命周期,执行完onCreate/onStart/onResume方法,UI渲染结束后便可以看到App的主界面

点击事件通过Binder IPC机制最终被转换成startActivity(intent),而App相关信息的解析以及处理则在安装的时候就已经完毕。当startActivity的时候,ActivityManagerService(AMS)的操作如下:

  • 第一步是intent解析。通过PackageManager的resolveIntent()方法,收集目标intent对象的信息。
  • 第二步是权限检查。通过grantUriPermissionLocked()方法,检查用户是否有足够的权限去调用intent的目标组件。
  • 第三步是创建新的任务。如果用户有足够的权限,ActivityManagerService就会检查目标Activity是否需要被加载在新的任务中。这个任务是根据Intent Flag来进行创建的。

最后检测进程记录表(ProgressRecord)是否存在。如果不存在,则需要通过ActivityManagerService来创建新的进程。

4. Application生命周期

此处讨论的是Application类,而非应用生命周期。Application的作用,用官方文档的话说。

Base class for maintaining global application state.

它的作用是维护全局的应用状态的基类。Application类是应用中最先被初始化的类,先于Activity等组件。它的生命周期与Activity有几分相似,都经历了创建销毁过程,只不过它是一个线性的过程,不存在一些恢复过程。

onCreate:

Called when the application is starting, before any activity, service, or receiver objects (excluding content providers) have been created.

当应用启动的时候调用,除了content providers之外,早于其他任何组件的创建。

onLowMemory:

This is called when the overall system is running low on memory, and actively running processes should trim their memory usage.

当整个系统内存不足的时候,活跃的进程需要减少它们的内存使用的时候,回调会被调用。系统调用此回调过后,会产生一次GC操作。

onTerminate:

This method is for use in emulated process environments.It will never be called on a production Android device, where processes are removed by simply killing them; no user code (including this callback) is executed when doing so.

在正式环境的真实设备上,不会被调用。由于系统结束进程采用的是kill的方法,因此不会产生相关的回调。

onTrimMemory:

Called when the operating system has determined that it is a good time for a process to trim unneeded memory from its process.

当系统检测到应用可以回收不需要的内存时,会产生此回调。例如应用处于后台,系统内存不足的时候。

5. Tips

5.1 回收机制

关于进程的管理,首先需要知道Low Memory Killer(LMK)这个概念。它是基于Linux的Out of Memory Mechanism(OOM机制)改进而来的。LMK是一个内核层组件,是一个进程杀手。它的主要作用是在系统低内存状态时,释放掉那些不太重要进程的内存,让系统更加流畅。

每一个进程根据其重要性,都包含一个oom_adj值,AMS会去动态的更新oom_adj这个值。当系统处在低内存状态时,LMK会根据oom_adj这个值,去杀死相关的进程。oom_adj值得范围是-17~15,一个进程的oom_adj值越高,它被杀死的概率就越大。

整个过程就是AMS更新oom_adj值,LMK去挑选并杀死进程。

5.2 ActivityManagerService的主要功能包括哪些?

可以看到,在安装App以及启动App的过程中,都有AMS的大量参与,它的主要功能包括以下几部分:

  • 统一调度各应用程序的Activity
  • 内存管理
  • 进程管理

5.3 App的安装过程是怎样的?

Apk其实就是一个压缩包,系统安装App的过程,其实就是资源的解析、拷贝以及验证等过程。

PackageManagerService会将App中的Manifest信息解析出来,并持久化,当用户点击桌面icon的时候,系统就会知道该启动哪个组件。PMS安装App,最后底层调用的也是adb命令来执行的。

大致来说,整个流程是,解析apk文件,执行安装过程,最后更新UI。

5.4 onLowMemory与onTrimMemory区别?

onLowMemory与onTrimMemory都是可以进行内存回收操作的地方,两者不同之处有以下几点:

  • 两者API level不同,onLowMemory在API level 1就被添加了,而onTrimMemory是在API level 14中被添加的。当然,对于现在最低版本都是从十几开始支持的,完全可以直接使用onTrimMemory。
  • 两者的触发时机不同,onLowMemory是在系统出现低内存状况时被触发,而onTrimMemory则是在置于后台而内存不足时被触发。

对于API 14以上的条件下,onTrimMemory在TRIM_MEMORY_COMPLETE级别跟onLowMemory可以等同。

猜你喜欢

转载自blog.csdn.net/ly0724ok/article/details/122128873