Android 面试题汇总

前言

本篇文章为面试题总结,文中出现的知识可能比较零散,读者可以通过目录选择性阅读。

由于作者水平有限,难免会有疏漏的地方,如果您发现了不正确的地方,可以发送邮件(yaoluhao@sina.com)于我,收到邮件后,我会及时处理问题。

愿您能不吝赐教,吾定当感激涕零。

Android 横竖屏切换时 Activity 的生命周期

默认情况下,Activity 就会被销毁并且重新创建;另外,可以通过配置参数阻止系统重新创建 Activity。

横竖屏切换,系统配置发生改变,Activity 会被销毁并重新创建。

  • 销毁时
    • onPause、onStop、onDestroy 均会被调用,系统会调用 onSaveInstanceState 来保存当前 Activity 的状态。
    • onSaveInstanceState 调用时机在 onStop 之前,和 onPause 没有严格的顺序。
    • 注意:onSaveInstanceState 只有在 Activity 异常终止的情况下才会被调用,正常情况下系统不会调用这个方法。
  • 重建时
    • 系统调用 onRestoreInstanceState,并且吧 Activity 销毁时 onSaveInstanceState 方法所保存的 Bundle 对象作为参数同时传递给 onRestoreInstanceState 和 onCreate 方法。
    • onRestoreInstanceState 调用时机在 onStart 之后。

同时,我们要知道,在 onSaveInstanceState 和 onRestoreInstanceState 中,系统自动为我们做了一定的恢复工作。当 Activity 在异常情况下需要重新创建时,系统会默认为我们保存当前 Activity 的视图结构,并且在 Activity 重启后为我们恢复这些数据,比如文本框中用户输入的数据、ListView 滚动的位置等,这些 View 相关的状态,系统都能默认为我们恢复。具体针对某一个特定的 View 系统能为我们恢复哪些数据,可以查看 View 的源码。和 Activity 一样,每个 View 都有在 onSaveInstanceState 和 onRestoreInstanceState 这连个方法,看他们的具体实现,就能知道系统能够自动为每个 View 恢复哪些数据。

关于保存和恢复 View 层次结构,系统的工作流程如下:首先 Activity 被意外终止时,Activity 会调用 onSaveInstanceState 去保存数据,然后 Activity 会委托 Window 去保存数据,接着 Window 在委托它上面的顶层容器去保存数据。顶层容器是一个 ViewGroup,一般来说它很可能是 DecorView。最后顶层容器去保存数据再去一一通知它的子元素来保存数据,这样完成了整个数据保存过程,这是一种典型的委托思想,还有比如 View 的绘制过程、事件分发等都是采用类似的思想。

关于接收位置:可以选择 onRestoreInstanceState 或者 onCreate,二者的区别是:onRestoreInstanceState 一旦被调用,其参数Bundle savedInstanceState 一定有值,不需要额外地非空判断;但 onCreate 正常启动时,Bundle savedInstanceState 为 null,所以必须额外判断。官方建议采用 onRestoreInstanceState 去恢复数据。

配置 configChanges 属性可以阻止 Activity 屏幕旋转时重新创建,只需要添加 orientation 这个值即可。configChanges 属性值有很多,其中比较常用的值有 locale、orientation 和 keyboardHidden。在众多的值当中,比较特殊的是 screenSize 和 smallestScreenSize,他们在 minSDKVersion 和 targetSDKVersion 有一个大于 13 时,会导致旋转屏幕事 Activity 重启,所以除了 orientation,还需要加上 screenSize

Activity 的启动过程

Activity 的启动过程可以从 Context 的 startActivity 说起,其实现是 ContextImpl 的 startActivity,其内部通过 Instrumtation 来尝试启动 Activity,这是跨进程过程,它会调用 AMS (ActivityManagerService) 的 startActivity 方法,当 AMS 校验完 Activity 的合法性后,会通过 ApplicationThread 回调到我们的进程,这也是一次跨进程过程,而 ApplicationThread 就是一个 Binder。回调逻辑是在 Binder 线程池中完成,所以需要通过调用 Handler 切回 UI 线程,在 LAUNCH_ACTIVITY 中对应着 handleLaunchActiviy,在这个方法里完成了 Activity 创建和启动。在 Activity 的 onResume 中,Activity 的内容将开始渲染到 Window 上面,然后开始绘制直到我们可以看到。

Android 性能优化最佳实践

  1. 布局优化
    1. 如果父控件有背景颜色,也是自己需要的颜色,那么子控件就不必要加背景颜色。
    2. 如果每个子控件的颜色都不太一样,而且可以完全覆盖父控件,那么就不需要再父控件上加背景颜色。
    3. 尽量减少不必要的嵌套。
    4. 能用 LinearLayout 和 FrameLayout,就不要用 RelativeLayout,因为 RelativeLayout 控件相对比较复杂,测绘也要耗时。
    5. 使用 include 和 merge 增加复用,减少层级。
    6. ViewStub 按需加载,更加轻便。
    7. 复杂界面可选用 ConstraintLayout,可以有效减少层级。
  2. 绘制优化
    1. onDraw 中不要创建新的布局对象
    2. onDraw 方法中不要做耗时任务
  3. 内存优化
    1. 解决各个情况下的内存泄漏,注意平时代码的规范
  4. 启动速度优化
坚持原创技术分享,您的支持将鼓励我继续创作!