Android高工面试题:Activity生命周期的变化对进程的优先级有什么影响?

在与腾讯面试官朋友的技术交谈中,我们谈到了上一次面试,他曾问一个高工的面试题:Activity生命周期的变化对进程的优先级有什么影响?,讨论半天,没有讨论出答案,这里我想写一篇博客表述我的看法。

PS:关于我


本人是一个拥有6年Android开发经验的老鸟,记得看完点赞,养成习惯,微信搜一搜「 程序猿养成中心 」关注这个喜欢写干货的程序员。

另外耗时两年整理收集的Android一线大厂面试完整考点PDF出炉,资料【完整版】已更新在我的【Github】,有面试需要的朋友们可以去参考参考,如果对你有帮助,可以点个Star哦!

Github地址:【https://github.com/733gh/xiongfan】


这里先看一下官网上Activity生命周期上对onStart的一段描述,onStart时候Activity就对用户可见了

Android高工面试题:Activity生命周期的变化对进程的优先级有什么影响?_第1张图片

同时你也可以在《Android开发艺术探索》上看到类似的描述

但是了解Activity启动流程源码的朋友都知道,ActivityThread的handleResumeActivity方法中,首先调用Activity的onResume方法,接着会调用Activity.makeVisible()在该方法中,DecorView真正完成了添加和显示这两个过程,到这里Activity的视图才能被看到。DecoreView和Window进行关联。有兴趣可以看看我这篇文章的分析。

void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            //DecoreView和WindowManager进行关联。
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
            //设置DecorView可见
        mDecor.setVisibility(View.VISIBLE);
    }

也就是说在onResume方法执行之后再调用Activity.makeVisible()方法,我们才能真正用肉眼看到我们的DecoreView,看到这里你这里不禁会产生一个疑问,那上面官网上的说法(onStart() 调用使 Activity 对用户可见)难道是错误的吗?

带着疑问我们继续在官网上找答案,在进程和生命周期这一章节上可以看到:

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

  1. 前台进程是用户目前执行操作所需的进程。在不同的情况下,进程可能会因为其所包含的各种应用组件而被视为前台进程。如果以下任一条件成立,则进程会被认为位于前台:
  • 它正在用户的互动屏幕上运行一个 Activity(其 onResume() 方法已被调用)。
  • 它有一个 BroadcastReceiver 目前正在运行(其 BroadcastReceiver.onReceive() 方法正在执行)。
  • 它有一个 Service 目前正在执行其某个回调(Service.onCreate()Service.onStart()Service.onDestroy())中的代码。
  1. 系统中只有少数此类进程,而且除非内存过低,导致连这些进程都无法继续运行,才会在最后一步终止这些进程。通常,此时设备已达到内存分页状态,因此必须执行此操作才能使用户界面保持响应。

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

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

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

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

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

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

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

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

可以看到在屏幕上运行时一个Activity的onResume的方法已被调用,此时处于前台进程;可见进程的一个符合条件:它正在运行的 Activity 在屏幕上对用户可见,但不在前台,然后再对比上面对onStart的描述(onStart() 调用使 Activity 对用户可见,因为应用会为 Activity 进入前台并支持互动做准备),这下子你就豁然开朗了,这里的onStart的可见指的是可见进程的可见,而不是真正意义上的肉眼可见

“onPause此方法表示 Activity 不再位于前台(尽管在用户处于多窗口模式时 Activity 仍然可见)”,“如果您的 Activity 不再对用户可见,说明其已进入“已停止”状态,因此系统将调用 onStop() 回调”,以上都是官方的描述,我们可以打印一下手机中的这些进程,使用adb shell dumpsys meminfo命令,设备是android 10华为手机。

Android高工面试题:Activity生命周期的变化对进程的优先级有什么影响?_第2张图片

可以看到分别对应我们的前台进程,可见进程,服务进程和缓存进程,其中服务进程还分为A Services和B Services。其实远远不止这么多的进程级别区分,我自己的App打开后,然后点击home键退到后台,此时属于Previous进程(后台进程)级别(com.jackie.testdialog),如果我打开App后,点击返回键退出,这个时候我的App进程就变成了Cached进程级别了。

讲了这么多,你可能觉得一直没有一个量化的数字,进程的级别(oom_adj)的取值范围是多少,在Android7.0之后,ADJ采用100,200,300等数字。下面是基于android9的区分:

ADJ级别 取值 含义
NATIVE_ADJ -1000 native进程
SYSTEM_ADJ -900 仅指system_server进程
PERSISTENT_PROC_ADJ -800 系统persistent进程
PERSISTENT_SERVICE_ADJ -700 关联着系统或persistent进程
FOREGROUND_APP_ADJ 0 前台进程
VISIBLE_APP_ADJ 100 可见进程
PERCEPTIBLE_APP_ADJ 200 可感知进程,比如后台音乐播放
BACKUP_APP_ADJ 300 备份进程
HEAVY_WEIGHT_APP_ADJ 400 重量级进程
SERVICE_ADJ 500 服务进程
HOME_APP_ADJ 600 Home进程
PREVIOUS_APP_ADJ 700 上一个进程
SERVICE_B_ADJ 800 B List中的Service
CACHED_APP_MIN_ADJ 900 不可见进程的adj最小值
CACHED_APP_MAX_ADJ 906 不可见进程的adj最大值

开发者应该减少在保活上花心思,更应该在优化内存上下功夫,因为在相同ADJ级别的情况下,系统会选择优先杀内存占用的进程。当然你也可以手动去测试App的进程级别,不过过程可能有点麻烦,可以参考这篇文章。

小结

当界面只有一个Activity时,它进入onStart和onPause时是可见进程,进入onResume时是前台进程,打开后点击Home键退到后台这个时候是Previous进程(后台进程),如果直接点击返回键退出Activity,这个时候是缓存进程;

如果有多个Activity(注意这个时候只有app从后台任务进入前台,或者点击Home键退到后台这两种场景;因为app在前台运行时都是前台进程),栈顶的的Activity进入onStart和onPause时是可见进程,进入onResume后是前台进程,点击Home键退到后台时是Previous进程(大家常说的后台进程)。

Github地址:【https://github.com/733gh/xiongfan】

你可能感兴趣的:(Android高工面试题:Activity生命周期的变化对进程的优先级有什么影响?)