应用启动概述
用户希望应用能够及时响应并快速加载。启动时间过长的应用不能满足这个期望,并且可能会令用户失望。这种糟糕的体验可能会导致用户在应用商店针对你的应用给出很低的评分,甚至完全弃用你的应用。
提高程序的启动速度意义重大,很显然,启动时间越短,用户才越有耐心等待打开这个APP进行使用,反之启动时间越长,用户则越有可能来不及等到APP打开就已经切换到其他APP了。程序启动过程中的那些复杂错误的操作很可能导致严重的性能问题。Android系统本身会根据用户的操作行为调整程序的显示策略,用来提高程序的显示性能。例如,一旦用户点击桌面图标,Android系统会立即显示一个启动窗口,这个窗口会一直保持显示直到画面中的元素成功加载并绘制完第一帧。这种行为常见于程序的冷启动,或者程序的热启动场景(程序从后台被唤起或者从其他APP界面切换回来)。那么关键的问题是,用户很可能会因为从启动窗口到显示画面的过程耗时过长而感到厌烦,从而导致用户没有来得及等程序启动完毕就切换到其他APP了。更严重的是,如果启动时间过长,可能导致程序出现ANR。我们应该避免出现这两种糟糕的情况。
所以,我们必须优化应用的启动时间,提高用户的使用体验。
应用启动内部机制及启动状态分类
从技术角度来说,当用户点击桌面图标开始,系统会立即为这个APP创建独立的专属进程,然后显示启动窗口,直到APP在自己的进程里面完成了程序的创建以及主线程完成了Activity的初始化显示操作,再然后系统进程就会把启动窗口替换成APP的显示窗口。
在创建应用和创建 Activity 的过程中都有可能会出现性能问题。
热启动
应用的热启动比冷启动简单得多,开销也更低。在热启动中,系统的所有工作就是将您的 Activity 带到前台。只要应用的所有 Activity 仍驻留在内存中,应用就不必重复执行对象初始化、布局膨胀和呈现。
但是,如果一些内存为响应内存整理事件(如 onTrimMemory())而被完全清除,则需要为了响应热启动事件而重新创建相应的对象。
热启动显示的屏幕上行为和冷启动场景相同:在应用完成 Activity 呈现之前,系统进程将显示空白屏幕。
温启动
温启动包含了在冷启动期间发生的部分操作;同时,它的开销要比热启动高。有许多潜在状态可视为温启动。例如:
- 用户在退出应用后又重新启动应用。进程可能已继续运行,但应用必须通过调用 onCreate() 从头开始重新创建 Activity。
- 系统将您的应用从内存中逐出,然后用户又重新启动它。进程和 Activity 需要重启,但传递到 onCreate() 的已保存的实例 state bundle 对于完成此任务有一定助益。
获取启动时间
要想提升APP启动时的性能,加快启动速度,首先我们应该正确的获取和分析应用的启动时间和性能。
为了正确诊断应用的启动时间和性能,我们可以跟踪一些显示应用启动所需时间的指标。
使用命令行方式获取
我们可以通过adb命令,使用am服务来获取冷启动时间:
或
- -S表示每次启动前先强行停止;
- -R表示重复测试次数。
- -c 和 -a 为可选参数,可以为 intent 指定 和 。
执行结果如下:
通过logcat获取
在 Android 4.4(API 级别 19)及更高版本中,logcat 包含一个输出行,其中包含名为 Displayed 的值。此值代表从启动进程到在屏幕上完成对应 Activity 的绘制所用的时间。这个方法比较适合测量程序的启动时间。
告的日志行类似于以下示例:
以上时间包含以下事件序列:
- 启动进程。
- 初始化对象。
- 创建并初始化 Activity。
- 扩充布局。
- 首次绘制应用。
注意:要在 Android Studio 中查找经过的时间,必须在 logcat 视图中停用过滤器。停用过滤器是必要的,因为提供此日志的是系统服务器,不是应用本身。
这个信息在 Activity 窗口完成所有的启动事件之后,第一次绘制的时候输出。这个时间包括了从启动进程到第一次布局与绘制的所有时间。这基本上是你需要知道的主要时间。它不包含用户点击app图标然后系统开始准备启动activity的时间,这是ok的,因为作为一个开发者你无法影响这个时间,所以没有必要去测量它。
在 logcat 中停用过滤器并查找 Displayed 值:
logcat 输出的示例:
logcat 输出有时包含 total 时间,如上文中所述。
注意:在4.4上调用 reportFullyDrawn() 方法会崩溃(但是 log 还是能正常打印),提示需要 UPDATE_DEVICE_STATS 权限 ,但是这个权限只有系统 app 才能授权。解决的办法是添加 try…catch 块保护。
使用 CPU profiler 工具分析耗时
前面的方法提供了启动耗时的总时间,可是却无法提供具体的耗时细节。为了获取具体的耗时分布情况,我们可以使用 CPU profiler 工具来进行详细的测量。
详情请见:《Android Studio CPU profiler性能分析工具介绍和使用详解》
使用 Systrace 工具分析耗时
我们也可以使用Systrace工具来分析启动耗时。
详情请见:《性能分析工具Systrace的使用详解》
常见的启动性能问题分类及常见优化方案
查找瓶颈的一个好方法是使用 Android Studio CPU 性能剖析器。详情请见:《Android Studio CPU profiler性能分析工具介绍和使用详解》
密集型应用初始化
在当前 APP 开发中,由于我们使用了大量第三方 SDK 以及一些大型组件库,它们通常会被要求在 Application 中进行初始化工作,这就造成了“密集型应用初始化”非常常见。
如果我们在代码中替换 Application 对象,并在初始化该对象过程中执行密集工作或复杂逻辑时,启动性能可能会受影响。如果我们的应用子类执行尚不需要完成的初始化,则应用可能会在启动过程中浪费时间,并且有些初始化可能完全没有必要。
在 Application 初始化的地方做太多繁重的事情是可能导致严重启动性能问题的元凶之一。Application 里面的初始化操作不结束,其他任意的程序操作都无法进行。
有时候,我们会一股脑的把绝大多数全局组件的初始化操作都放在 Application 的 onCreate 里面,但其实很多组件是需要做区队对待的,有些可以做延迟加载,有些可以放到其他的地方做初始化操作,特别需要留意包含 Disk IO 操作, 络访问等严重耗时的任务,他们会严重阻塞程序的启动。
密集型 Activity 初始化
创建 Activity 通常需要进行大量的高开销工作。提升 Activity 的创建速度是优化 APP 启动速度的首要关注目标。从桌面点击 APP 图标启动应用开始,程序会显示一个启动窗口等待 Activity 的创建加载完毕再进行显示。在 Activity 的创建加载过程中,会执行很多的操作,例如设置页面的主题,初始化页面的布局,加载图片,获取 络数据,读写 Preference 等等。

上述操作的任何一个环节出现性能问题都可能导致画面不能及时显示,影响了程序的启动速度。
通常有机会优化这项工作以实现性能改进。
此类常见问题包括:
- 扩充大型或复杂的布局。
- 阻止磁盘上的屏幕绘制或 络 I/O。
- 加载和解码位图。
- 栅格化 VectorDrawable 对象。
- 初始化 Activity 的其他子系统。
- 主线程的耗时任务操作。
如何诊断问题实证明,在这种情况下,方法跟踪记录和内嵌跟踪记录同样很有用。
问题解决方案:
- 优化布局耗时:一个布局层级越深,里面包含需要加载的元素越多,就会耗费更多的初始化时间。解决此问题的示例方法如下:
- 通过减少冗余或嵌套布局,展平你的视图层次结构。
- 不要膨胀在启动期间无需显示的界面部分,而是使用 ViewStub 对象作为应用可以在更合适的时间膨胀的子层次结构的占位符。
- 异步延迟加载:一开始只初始化最需要的布局,异步加载图片,非立即需要的组件可以做延迟加载。解决此类问题的示例方法如下:
- 转移所有资源初始化,以便应用可以在其他线程上延迟执行。
- 允许应用加载并显示你的视图,稍后再更新依赖于位图和其他资源的可视属性。
总结
- 应用启动内部机制是什么,启动状态可以分为:冷启动、温启动和热启动三种类型,以及分别介绍了每种启动类型的具体启动过程。
- 第二部分介绍了通过什么方式来获取应用的启动时间,以及如何对启动耗时进行分析。
- 最后一部分介绍了常见的启动性能问题分类及常见优化方案。
PS:性能优化专栏:《Android性能》持续更新中……
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!