用户希望应用能够及时响应并快速加载。启动时间过长的应用无法满足这个期望,并且可能会令用户失望。甚至完全抛弃你的应用。
确定应用启动时间的两个重要指标是初步显示所用时间 (TTID) 和完全绘制所用时间 (TTFD)。TTID 是显示第一帧所用的时间,TTFD 则是应用达到可全面互动的状态所用的时间。两者同样重要,因为 TTID 让用户知道应用正在加载,TTFD 表示用户等待多长时间才能实际使用应用。如果其中任一时间过长,用户都可能会在应用完全加载之前退出应用。
冷启动
冷启动是指应用从头开始启动。这意味着,系统进程在冷启动后才创建应用进程。发生冷启动的情况包括应用自设备启动后或系统终止应用后首次启动。
这种启动给最大限度地减少启动时间带来了最大的挑战,因为系统和应用要做的工作比在其他启动状态下更多。
在冷启动开始时,系统有以下三项任务:
- 加载并启动应用。
- 在启动后立即显示应用的空白启动窗口。
- 创建应用进程。
系统一创建应用进程,应用进程就负责后续阶段:
- 创建应用对象。
- 启动主线程。
- 创建主 activity。
- 膨胀视图。
- 创建屏幕布局。
- 执行初步绘制。
当应用进程完成第一次绘制时,系统进程就会换掉显示的后台窗口,将其替换为主 activity。此时,用户可以开始使用应用。
图 1 显示系统进程和应用进程之间如何交接工作。
图1.png
图 1. 以可视化方式呈现应用冷启动的重要部分。
在创建应用和创建 activity 的过程中可能会出现性能问题。
应用创建
当应用启动时,空白启动窗口将保留在屏幕上,直到系统首次完成应用绘制。此时,系统进程会切换应用的启动窗口,让用户与应用互动。
如果在自己的应用中替换了 Application.onCreate(),系统将对应用对象调用 onCreate() 方法。之后,应用生成主线程(也称为界面线程),并用其执行创建主 activity 的任务。
从此时开始,系统级和应用级进程根据应用生命周期阶段继续运行。
Activity创建
在应用进程创建 activity 后,activity 将执行以下操作:
- 初始化值。
- 调用构造函数。
- 根据 activity 的当前生命周期状态,相应地调用回调方法,如
Activity.onCreate()。
通常,onCreate() 方法对加载时间的影响最大,因为它执行工作的开销最高:加载和膨胀视图,以及初始化运行 activity 所需的对象。
初步显示所用时间
初步显示所用时间 (TTID) 指标用于测量应用生成第一帧所用的时间,包括冷启动期间的进程初始化、冷启动或温启动期间的 activity 创建,以及显示第一帧。
如何检索 TTID
Logcat 包含一个输出行,该行包含名为 Displayed 的值。此值代表从启动进程到在屏幕上完成对应 activity 的绘制所用的时间。经过的时间包括以下事件序列:
- 启动进程。
- 初始化对象。
- 创建并初始化 activity。
- 膨胀布局。
- 首次绘制应用。
报告的日志行类似于以下示例:
ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms
如果从命令行或在终端中跟踪 Logcat 输出,查找经过的时间很简单。如需在 Android Studio 中查找经过的时间,请在 Logcat 视图中停用过滤器。停用过滤器是必要的,因为提供此日志的是系统服务器,不是应用本身。
进行正确的设置后,可以搜索正确术语来查看时间。图 3 展示了一个 Logcat 输出示例,其中显示了如何停用过滤器(从下拉列表中选择“No filters”即可),并且在输出内容的倒数第二行显示了 Displayed 时间。
图5.png
图 5. 停用过滤器,并在 Logcat 中查找 Displayed 值。
在所有资源完全加载并显示之前,Logcat 输出中的 Displayed 指标不一定会捕获时间。它会省去布局文件中未引用的资源或被应用作为对象初始化一部分创建的资源。它之所以排除这些资源,是因为加载它们是一个内嵌进程,并且不会阻止应用的初步显示。
有时,Logcat 输出中的 Displayed 行中会包含用于显示总时间的附加字段。例如:
ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms (total +1m22s643ms)
在这种情况下,第一个时间测量值仅针对第一个绘制的 activity。total 时间测量值是从应用进程启动时开始计算,并且可以包含首次启动但未在屏幕上显示任何内容的另一个 activity。total 时间测量值仅在单个 activity 的时间和总启动时间之间存在差异时才会显示。
还可以使用 ADB Shell Activity Manager 命令运行应用来测量 TTID。示例如下:
adb [-d|-e|-s <serialNumber>] shell am start -S -W
com.example.app/.MainActivity
-c android.intent.category.LAUNCHER
-a android.intent.action.MAIN
Displayed 指标和以前一样出现在 Logcat 输出中。终端窗口会显示以下内容:
Starting: Intent
Activity: com.example.app/.MainActivity
ThisTime: 2044
TotalTime: 2044
WaitTime: 2054
Complete
-c 和 -a 为可选参数,可指定 <category> 和 <action>。
完全显示所用时间
完全显示所用时间 (TTFD) 指标用于测量应用生成具有完整内容的第一帧所用的时间,包括在第一帧之后异步加载的内容。一般情况下,这是从网络加载的主要列表内容(由应用报告)。
如何检索 TTFD
可以使用 reportFullyDrawn() 方法测量从应用启动到完全显示所有资源和视图层次结构所用的时间。在应用执行延迟加载时,此数据会很有用。在延迟加载中,应用不会阻止窗口的初步绘制,但会异步加载资源并更新视图层次结构。
由于延迟加载,应用的初步显示不包括所有资源,不妨将完全加载并显示所有资源及视图视为单独的指标。例如,界面可能已完全加载,并绘制了一些文本,但尚未显示应用必须从网络中提取的图片。
提高启动时间准确性说明了如何在应用达到可与用户进行互动的状态之前使用 FullyDrawnReporter 延迟调用 reportFullyDrawn。
当使用此 API 时,Logcat 显示的值为从创建应用对象到调用 reportFullyDrawn() 时所用的时间。以下是 Logcat 输出的示例:
system_process I/ActivityManager: Fully drawn {package}/.MainActivity: +1s54ms
Logcat 输出有时包含 total 时间,如初步显示所用时间中所述。
如果发现显示时间比希望的时间长,则可以尝试识别启动过程中的瓶颈。











网友评论