怎么计算apk的启动时间?
12 个回答
事实上是可以准确计算的.但是要分场景.
但是要分开游戏和应用. 大家都知道,在Android中,游戏开发和应用开发是两码事.所以我们需要分开来说.
1. 应用启动.
我们平时在写应用的时候,一般会指定一个mainActivity,用户在桌面上点击这个Activity的时候,系统会直接起这个Activity. 我们知道Activity在启动的时候会走onCreate/onStart/onResume.这几个回调函数.
许多书里讲过,当执行完onResume函数之后,应用就显示出来了...其实这是一种不准确的说法,因为从系统层面来看,一个Activity走完onCreate/onStart/onResume这几个生命周期之后,只是完成了应用自身的一些配置,比如window的一些属性的设置/ View树的建立(只是建立,并没有显示,也就是说只是调用了inflate而已) . 后面ViewRootImpl还会调用两次performTraversals ,初始化Egl以及measure/layout/draw.等.
所以我们定义一个Android应用的启动时间, 肯定不能在Activity的回调函数上下手.而是以用户在手机屏幕上看到你在onCreate的setContentView中设置的layout完全显示为准,也就是我们常说的应用第一帧.
上面扯得有点远,不感兴趣的话可以不看,下面直接说方法.
题主说的adb shell am start -w packagename/activity,是可以完全应用的启动时间的.不过也要分场景.
1.1 应用第一次启动
也就是我们常说的冷启动,这时候你的应用程序的进程是没有创建的. 这也是大部分应用的使用场景.用户在桌面上点击你应用的icon之后,首先要创建进程,然后才启动mainActivity.
这时候adb shell am start -w packagename/activity 返回的结果,就是标准的应用程序的启动时间:
➜ adb shell am start -W com.meizu.media.painter/com.meizu.media.painter.PainterMainActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.meizu.media.painter/.PainterMainActivity }
Status: ok
Activity: com.meizu.media.painter/.PainterMainActivity
ThisTime: 355
TotalTime: 355
WaitTime: 365
Complete
总共返回了三个结果,我们以WaitTime为准. (关于ThisTime/TotalTime/WaitTime的区别,各位可以自己去看源码.这里就不详细说了). WaitTime会返回从startActivity到应用第一帧完全显示这段时间.
1.2 应用非第一次启动
如果是你按Back键,并没有将应用进程杀掉的话,那么执行上述命令就会快一些,因为不用创建进程了,只需要启动一个Activity即可。这也就是我们说的应用热启动。
2. 游戏启动
游戏启动的话,就不适用用命令行的方法来启动了,因为从用户点击桌面图标到登录界面,既有系统的部分也有游戏自己的部分。
2.1 系统部分
游戏也有一个Activity,所以启动的时候还是会去启动这个Activity,所以系统启动部分也就是用户点击桌面桌面响应到这个Activity启动。
2.2 游戏部分
一般游戏的主Activity启动后,还会做一些比较耗时的事情,这时候你看到的界面是不能操作的,比如:加载游戏数据、联网更新数据、读取和更新配置文件、游戏引擎初始化等操作。从游戏开发的角度来看,到了真正用户能操作的界面才算是一个游戏真正加载完成的时间。
那么这个时间,就得使用Log来记录了,因为加载游戏数据、联网更新数据、读取和更新配置文件、游戏引擎初始化这些操作,都是游戏自己的逻辑,与系统无关,所以得由游戏自己定义加载完成的点。
3. 准确性
计算机最让人着迷的一点就是其准确性,1+1永远等于2,启动耗时多久就是多久,每一次可能不一样,但每一次的时间都是这一次的准确时间。
一些愚见,有想法可以一起交流
终于有空来填坑啦。关于应用启动速度,@
Gracker已经回答的很完善了。我补充下“adb shell am start -W ”这条命令得出的三个时间是如何计算出来的,了解了这个也就清楚了究竟哪个时间更准了。
“adb shell am start -W ”的实现在frameworks\base\cmds\am\src\com\android\commands\am\Am.java文件中。其实就是跨Binder调用ActivityManagerService.startActivityAndWait()接口(后面将ActivityManagerService简称为AMS),这个接口返回的结果包含上面打印的ThisTime、TotalTime时间。
WaitTime = endTime - startTime
startTime记录的刚准备调用startActivityAndWait()的时间点,endTime记录的是startActivityAndWait()函数调用返回的时间点,WaitTime = startActivityAndWait()调用耗时。
ThisTime、TotalTime的计算在frameworks\base\services\core\java\com\android\server\am\ActivityRecord.java文件的reportLaunchTimeLocked()函数中。
我们来解释下代码里curTime、displayStartTime、mLaunchStartTime三个时间变量。
curTime表示该函数调用的时间点.
displayStartTime表示一连串启动Activity中的最后一个Activity的启动时间点.
mLaunchStartTime表示一连串启动Activity中第一个Activity的启动时间点.
正常情况下点击桌面图标只启动一个有界面的Activity,此时displayStartTime与mLaunchStartTime便指向同一时间点,此时ThisTime=TotalTime。另一种情况是点击桌面图标应用会先启动一个无界面的Activity做逻辑处理,接着又启动一个有界面的Activity,在这种启动一连串Activity的情况下(知乎的启动就是属于这种情况),displayStartTime便指向最后一个Activity的开始启动时间点,mLaunchStartTime指向第一个无界面Activity的开始启动时间点,此时ThisTime!=TotalTime。这两种情况如下图:
在上面的图中,我用①②③分别标注了三个时间段,在这三个时间段内分别干了什么事呢?
在第①个时间段内,AMS创建ActivityRecord记录块和选择合理的Task、将当前Resume的Activity进行pause;
在第②个时间段内,启动进程、调用无界面Activity的onCreate()等、pause/finish无界面的Activity;
在第③个时间段内,调用有界面Activity的onCreate、onResume;
看到这里应该清楚 ThisTime、TotalTime、WaitTime三个时间的关系了吧。WaitTime就是总的耗时,包括前一个应用Activity pause的时间和新应用启动的时间;ThisTime表示一连串启动Activity的最后一个Activity的启动耗时;TotalTime表示新应用启动的耗时,包括新进程的启动和Activity的启动,但不包括前一个应用Activity pause的耗时。也就是说,开发者一般只要关心TotalTime即可,这个时间才是自己应用真正启动的耗时。
Event log中TAG=am_activity_launch_time中的两个值分表表示ThisTime、TotalTime,跟通过“adb shell am start -W ”得到的值是一致的。
最后再说下系统根据什么来判断应用启动结束。我们知道应用启动包括进程启动、走Activity生命周期onCreate/onResume等。在第一次onResume时添加窗口到WMS中,然后measure/layout/draw,窗口绘制完成后通知WMS,WMS在合适的时机控制界面开始显示(夹杂了界面切换动画逻辑)。记住是窗口界面显示出来后,WMS才调用reportLaunchTimeLocked()通知AMS Activity启动完成。
最后总结一下,如果只关心某个应用自身启动耗时,参考TotalTime;如果关心系统启动应用耗时,参考WaitTime;如果关心应用有界面Activity启动耗时,参考ThisTime。