【Android端 APP 启动时长获取】启动时长获取方案及具体实施

时间:2022-11-10 21:09:46

一、什么是启动时长?

1、启动时长一般包括三种场景,分别是:新装包的首次启动时长,冷启动时长、热启动时长

冷启动 和 热启动 :

(1)冷启动:当启动应用时,后台没有该程序的进程,此时启动的话系统会分配一个新的进程给应用。

(2)热启动:程序的进程依然存在,启动时通过已有进程启动进入到Activity显示页面的,就是热启动,或者从Android官网来看我们获取到的其实是温启动时长,就是Activity不存在的情况。

(3)新装包的启动时长:

新装包的启动时长,预估是最长的,并且在5.0以下及5.0以上的Android系统上的表现不同,因为:Android 5.0和更高版本使用名为ART的运行时,它原生支持从APK文件加载多个DEX文件。在应用安装时,它会执行预编译,扫描classes(..N).dex文件然后将其编译成单个.oat文件用于执行。而Android5.0以下的系统不支持,只能在程序点击启动之后,进行多个dex文件的加载,如果是在Android5.0以下的机型上获取时长,就能明显看到新装包启动时长要比其他启动时长都要长。

其中冷启动的过程如下图所示:

【Android端 APP 启动时长获取】启动时长获取方案及具体实施

系统开始启动Activity的时间,就是从start u0这个时间看的,准备启动这个Activity的时间,就是这条日志前面打印出来的这个时间:

12-14 16:45:51.483 I/ActivityManager( 1370): START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.ganji.android/.control.LaunchActivity bnds=[571,684][763,876] (has extras)} from uid 10021 on display 0

之后该Activity的第一帧展示出来的时候,会打印出Display的日志信息,如:

12-14 16:45:52.023 I/ActivityManager( 1370): Displayed com.ganji.android/.control.LaunchActivity: +521ms

从start u0到display第一帧的这个时间 包括了从启动进程到第一次布局与绘制的所有时间。这基本上是你需要知道的主要时间。它不包含用户点击app图标然后系统开始准备启动activity的时间,这是ok的

2、关于我们想要获取的三种场景的启动时长

(1)三种场景是启动的三种典型场景,这三种场景并没有包含将缓存和数据清除掉的情况;其中用户经历次数最多的是 “冷启动时长” 和“ 热启动时长”

(2)新装包的首次启动时长,一般情况下是最多的,在Android5.0以下系统上从APK文件加载多个dex文件的过程也包含在内。

冷启动时长居第二位,因为杀进程之后内存中的缓存会丢失,很多数据需要重新请求,并且需要重新启动进程;

热启动时长是最短的。

(3)我们想要获取启动时长的场景:

都是从初始的LaunchActivity页面进入到下一个的可操作的页面的时间;

比如以赶集网为例,新装包首次启动之后会跳转到选择城市的页面,我们需要获取的时长就是从 LaunchActivity的页面进入到SelectcityActivity的页面

因为用户一般会用到的场景是:选择城市进入到主页面,下次再启动都是直接进入到主页面,所以编写了uiautomator的脚本,能够通过UItest的方式选择城市;之后再分别获取 “冷启动时长” 和 “热启动时长”,即杀进程和正常退出程序的启动时长

二、如何获取?

1、因为通过logcat打印日志过滤ActivityManager的方式来获取启动时间,能够对其他APP也适用,因此最终确定用这种方式。

其中以赶集生活为例,新装包首次启动时长的获取,需要获取的是:从LaunchActivity的启动,到Displayed出来SelectCityActivity,刚开始用的是Displayed的后面的这个time的时长,但是跟开发确认之后,他说这个时间比用户的实际感知过程的时间还少一些,最终确定的是:

获取从start u0 LaunchActivity的时间到Display可操作的Activity的时间,拿到前面的时间节点之后,然后进行计算

以赶集网为例,三种场景下在 小米 4 + Android4.4.4的机型下,通过logcat抓到的日志如下:

新装包首次启动从Launch界面进入到选择城市的界面:

12-12 17:08:51.101 I/ActivityManager( 1191): START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.ganji.android/.control.LaunchActivity} from pid 5656

12-12 17:08:58.141 I/ActivityManager( 1191): Displayed com.ganji.android/.control.LaunchActivity: +6s803ms

12-12 17:08:59.041 I/ActivityManager( 1191): START u0 {flg=0x200000 cmp=com.ganji.android/.control.BetterCityActivity (has extras)} from pid 5668

12-12 17:08:59.711 I/ActivityManager( 1191): Displayed com.ganji.android/.control.BetterCityActivity: +654ms

热启动时长:

12-12 17:11:23.571 I/ActivityManager( 1191): START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.ganji.android/.control.LaunchActivity} from pid 6369

12-12 17:11:24.021 I/ActivityManager( 1191): Displayed com.ganji.android/.control.LaunchActivity: +264ms

12-12 17:11:25.311 I/ActivityManager( 1191): START u0 {cmp=com.ganji.android/.control.MainActivity} from pid 5668

12-12 17:11:27.311 I/ActivityManager( 1191): Displayed com.ganji.android/.control.MainActivity: +1s989ms

冷启动时长:

12-12 17:13:16.981 I/ActivityManager( 1191): START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.ganji.android/.control.LaunchActivity} from pid 6625

12-12 17:13:18.231 I/ActivityManager( 1191): Displayed com.ganji.android/.control.LaunchActivity: +1s237ms

12-12 17:13:19.231 I/ActivityManager( 1191): START u0 {cmp=com.ganji.android/.control.MainActivity} from pid 6637

12-12 17:13:22.811 I/ActivityManager( 1191): Displayed com.ganji.android/.control.MainActivity: +3s556ms

三种场景下,都需要从start u0 cmp = LaunchActivity的时间开始,到Displayed 下一个Activity的时间结束

2、当然,也可以通过录屏的方式,录屏的命令是:

adb shell screenrecord --bugreport /sdcard/launch.mp4

之后按Ctrl+C结束,然后将视频文件导出,之后使用可按帧播放的播放器即可。

使用播放器播放的时候,可以从用户点击屏幕发白发亮的那一刻算是开始时间,这个时间可以精确到ms,之后结束的话,就以你到达的页面,并且可以等待异步加载的都完成为止。

这个与 1 中的方法相比,获取的时间是会更长一些,因为包含了点击之后有了用户的touch操作,之后 系统响应,再到系统开始响应启动进程,创建Activity,加载视图,度量并进行Activity的整体显示的过程

并且通过logcat的方式,只能用在Activity从A跳转到B的这种情况,如果是Activity不变化,只是页面数据在刷新,logcat就无法通过ActivityManager打印出日志。

最终方案确定:

最终根据我们与开发的沟通,及通过这两种方式的对比,也为了后续能够方便通过程序写成自动化的形式,选择了第一种,并且增加了通过uiautomator完成城市选择和点击back键正常退出app的UI自动化的case来支持自动化实现。

三、最后确定的方案,方案的实施和工具实现

针对以上需求:

1、方便获取竞品的对比数据,需要在同一机型上执行,并且最好执行多次,选择一个平均值

2、方便方案的通用性,以及自动化实现

通过程序实现,主要包含以下过程:

1、自动装包

2、启动logcat,清除logcat的缓存,执行 adb  logcat -v time -s ActivityManager ,并将输出内容输出到某个文件

3、启动应用程序,通过adb shell am start LaunchActivity的方式,获取新装包的启动时间;之后通过uiautomator执行UI操作,完成热启动时间的获取,之后通过adb shell am force-stop pkg包名的方式杀进程,再启动完成冷启动时间的获取

3、将日志文件从手机中pull出来

4、对日志文件进行分析,因为前面都增加了-v time 的选项,log中能够打印出具体到ms的时间,可以根据你设置的规则,直接获取某条日志前面的时间

5、获取到开始时间和结束时间之后,用结束时间减去开始时间即可

备注:

参考资料————————————

APP的安装流程:

http://www.cnblogs.com/sunshine-anycall/p/3544345.html

Android APP的启动过程:

http://blog.csdn.net/freshui/article/details/8695463

http://www.androidchina.net/3851.html

http://mp.weixin.qq.com/s?__biz=MzA4MzEwOTkyMQ==&mid=2667376274&idx=1&sn=b35396dc9e2749da076b94979c9c5424&chksm=84f33fdcb384b6cac52b98a29cf379178afe69d6761e92b4b7ad35627c722ac3dbf6887a28c7&mpshare=1&scene=1&srcid=1011ObiSQI9cClmRpq89H8XD#rd

APK预编译提取Odex:http://blog.csdn.net/huangyabin001/article/details/46973625

Android MultiDex实践:https://gold.xitu.io/entry/5705b2712e958a0057a5f735

Android应用打破65K方法数限制:http://www.infoq.com/cn/news/2014/11/android-multidex

Android developer的官网针对Launch-Time Performance的内容如下:https://developer.android.com/topic/performance/launch-time.html

http://blog.csdn.net/huangyabin001/article/details/46973625