2023年6月27日发(作者:)
如何统计AndroidApp启动时间随着App的逻辑不断庞⼤,⼀不注意就会将耗时的操作放置在应⽤启动过程之中,导致应⽤启动速度越来越慢,⽤户体验也越来越差。优化启动速度是⼏乎所有⼤型App应⽤开发者需要考虑的问题。优化启动速度之前⾸先需要准确测量App启动时间,这样有利于我们更准确可量化地看出优化效果,也可以指导我们进⾏持续优化。转载请注明出处:Lawrence_Shen同时可以参考2019年的性能分析⽂章:Android性能分析&启动优化- 使⽤命令⾏⽅式使⽤命令⾏⽅式统计多次启动某个Activity的平均⽤时可以在shell中执⾏如下指令:adb shell am start -S -R 10 -W /.MainActivity其中-S表⽰每次启动前先强⾏停⽌,-R表⽰重复测试次数。每⼀次的输出如下所⽰信息。Stopping: rting: Intent { act= cat=[ER] cmp=/.MainActivity }Status: okActivity: /.MainActivityThisTime: 1059TotalTime: 1059WaitTime: 1073Complete其中TotalTime代表当前Activity启动时间,将多次TotalTime加起来求平均即可得到启动这个Activity的时间。缺点1. 应⽤的启动过程往往不只⼀个Activity,有可能是先进⼊⼀个启动页,然后再从启动页打开真正的⾸页。某些情况下还有可能中间经过更多的Activity,这个时候需要将多个Activity的时间加起来。2. 将多个Activity启动时间加起来并不完全等于⽤户感知的启动时间。例如在启动页可能是先等待某些初始化完成或者某些动画播放完毕后再进⼊⾸页。使⽤命令⾏统计的⽅式只是计算了Activity的启动以及初始化时间,并不能体现这种等待任务的时间。3. 没有在对应的Activity声明中指定或者属性没有android:exported="true"的Activity不能使⽤这种命令⾏的形式计算启动时间。-思考更准确的⽅式以上基于命令⾏的⽅式存在诸多问题,迫使我们思考怎样才能得到从⽤户⾓度上观察更准确的启动时间。在尝试其他⽅法之前,我们先定义⼀下怎样才是从⽤户⾓度上观察的启动时间。冷启动、热启动(注意不是官⽅的定义,是我们从⽤户⾓度考虑的定义)冷启动时间:冷启动表⽰⽤户⾸次打开应⽤,这时进程还没创建,包含了Application创建的过程。冷启动时间指从第⼀次⽤户点击Launcher中的应⽤图标开始,到⾸页内容全部展⽰出来的时间。热启动时间:热启动表⽰⽤户在⾸页按了返回,⾸页Activity已经Destroy,不过Application仍在内存中存在,对应的进程并没有被杀掉,不包含Application创建过程。热启动时间指在Application仍然存在的情况下,从⽤户点击桌⾯图标,到⾸页内容全部展⽰出来的时间。App启动流程要优化以及分析启动时间,需要先了解App的启动流程。以冷启动为例⼦,Application以及Activity的启动流程如下,参考⽂章[3][4][5][6]:app启动流程更为直观和简单的流程图参考Colt McAnlis在Android Performance Patterns Season 6中的表述。有兴趣的同学可以点击链接看看(Youtube链接)。app启动流程by Colt McAnlis从流程图以及参考Colt McAnlis的Android Performance Patterns[6]得知,在冷启动的过程中,⾸先会通过AMS在System进程展⽰⼀个Starting Window(通常情况下是个⽩屏,可以通过设置Application的theme修改),接着AMS会通过Zygote创建应⽤程序的进程,并通过⼀系列的步骤后调⽤Application的attachBaseContext()、onCreate()然后最终调⽤Activity的onCreate()以及进⾏View相关的初始化⼯作。在Activity展⽰出来后会替换掉之前的Starting Window,这样启动过程结束。如何加log参考[1]发现在Activity中onWindowFocusChanged()⽅法是最好的Activity对⽤户可见的标志,因此综合上⼀节的分析,我们可以考虑在Application的attachBaseContext()⽅法中开始计算冷启动计时,然后在真正⾸页Activity的onWindowFocusChanged()中停⽌冷启动计时,这样就可以初步得到应⽤的冷启动时间。public void onWindowFocusChanged(boolean hasFocus)Called when the current
of the activity gains or loses focus. This is the best indicator ofwhether this activity is visible to the user.为了⽅便统计,设置⼀个Util类专门做计时,添加的代码如下:/** * 计时统计⼯具类 */public class TimeUtils { private static HashMap sCalTimeMap = new HashMap<>(); public static final String COLD_START = "cold_start"; public static final String HOT_START = "hot_start"; public static long sColdStartTime = 0; /** * 记录某个事件的开始时间 * @param key 事件名称 */ public static void beginTimeCalculate(String key) { long currentTime = tTimeMillis(); (key, currentTime); } /** * 获取某个事件的运⾏时间 * * @param key 事件名称 * @return 返回某个事件的运⾏时间,调⽤这个⽅法之前没有调⽤ {@link #beginTimeCalculate(String)} 则返回-1 */ public static long getTimeCalculate(String key) { long currentTime = tTimeMillis(); Long beginTime = (key); if (beginTime == null) { return -1; } else { (key); return currentTime - beginTime; } } /** * 清除某个时间运⾏时间计时 * * @param key 事件名称 */ public static void clearTimeCalculate(String key) { (key); } /** * 清除启动时间计时 */ public static void clearStartTimeCalculate() { clearTimeCalculate(HOT_START); clearTimeCalculate(COLD_START); sColdStartTime = 0; }}然后在Application的attachBaseContext()⽅法中添加如下代码:@Overrideprotected void attachBaseContext(Context base) { BaseContext(base); if (/**如果是主进程**/) { imeCalculate(_START); }}在第⼀个Activity的onCreate()⽅法中添加如下代码:@Overrideprotected void onCreate(Bundle savedInstanceState) { te(savedInstanceState); calculateStartTime(); ....}private void calculateStartTime() { long coldStartTime = eCalculate(_START); // 这⾥记录的artTime是指Application启动的时间,最终的冷启动时间等于Application启动时间+热启动时间 tartTime = coldStartTime > 0 ? coldStartTime : 0; imeCalculate(_START);}在真正的⾸页Activity的
onWindowFocusChanged()⽅法中添加如下代码:@Overridepublic void onWindowFocusChanged(boolean hasFocus) { if (hasFocus && /**没有经过⼴告或者引导页**/) { long hotStartTime = eCalculate(_START); if (tartTime > 0 && hotStartTime > 0) { // 真正的冷启动时间 = Application启动时间 + 热启动时间 long coldStartTime = tartTime + hotStartTime; // 过滤掉异常启动时间 if (coldStartTime < 50000) { // 上传冷启动时间coldStartTime
} } else if (hotStartTime > 0) { // 过滤掉异常启动时间 if (hotStartTime < 30000) { // 上传热启动时间hotStartTime
} } }}避免坑的Checklist上⾯的分析给了我们初步的加log的起始和结束点,然⽽在实际的统计中会发现得到的数据有20%左右是不准确的,体现在计时数据⾮常⼤,有些甚⾄会显⽰冷启动时间超过⼀天。经过分析,在计算启动计时的时候需要注意⼀些问题。以下列举⼀下添加log时候需要注意的checklist。1. 应⽤在启动过程可能会有⼴告(我们的业务是有道词典),第⼀次启动会有引导页,需要根据业务情况标记在没有⼴告、没有引导页的时候才计算。这种情况要注意在⾮正常启动的时候忽略启动时间统计。2. 由于词典⾸页之前还有⼏个Activity,在没到⾸页Activity之前如果过早的返回,会出现冷启动时间过长的问题。这是因为词典返回的时候并没有杀掉进程,⽽时间统计信息是保存在内存中的,⽽等下次再进⼊的时候因为是热启动不会重新开始冷启动计时。这导致了这次热启动实际上打log的时候发现有上次冷启动的开始时间,算成了冷启动,⽽且因为启动时间是上⼀次的,所以这次冷启动log的时间⽐实际时间长。这种情况要注意在⾸页Activity之前的其他ActivityonPause()⽅法中调⽤tartTimeCalculate();清除计时。3. 除了正常的启动流程,应⽤还有很多可能会导致Application的创建的⼊⼝,例如点击桌⾯⼩插件、系统账号同步、Deep Link跳转、直接进⼊设置了的Activity、push达到等。我们需要检查所有有可能引起Application创建,但是不是正常启动流程的地⽅,调⽤tartTimeCalculate();清除计时,避免引起冷启动时间计算过长错误的问题。- 使⽤第三⽅⼯具为了测试启动的过程中哪些⽅法⽐较耗时,我们可以使⽤Android Studio中集成的Android Monitor提供的Method Tracering或者Systrace。不过在实践中发现,有另外⼀个nimbledroid⼯具使⽤更加简便且能更明确指出耗时的地⽅。上传了应⽤之后会⾃动分析情景如下图所⽰。其中会⾃动检测出⾸页的Activity并且给出冷启动的启动情况。情景分析点击进⼊Cold Startup的情景可以看到主要耗时的⽅法如下图。情景详细耗时统计⾄于为什么nimbledroid会知道那个是我们⾸页的Activity,官⽹上解析如下:We use a heuristic to tell when an app finishes startup by detecting when (1) the main Activity has beendisplayed and (2) things like animated progress bars in the main Activity have stopped. Based on ourexperiments, this heuristic works in most cases.点击进⼊某个⽅法,可以看到这个⽅法具体是由于调⽤了哪个⼦⽅法导致了耗时的问题。耗时⽅法详细通过nimbledroid这个⼯具,我们可以⽐较轻松地发现⼀些⽐较明显的问题,并可以指导我们进⾏启动优化。同时nimbledroid还⽀持MemoryLeaks、⽹络监测以及结果分享等⼀些功能,更多的功能有待读者继续发现。- 后记统计和分析启动时间有利于指导我们优化启动时间。以上介绍了有道词典在进⾏启动优化中的分析过程。通过详细了解Android应⽤启动的流程,进⾏准确的log记录,并且结合第三⽅⼯具,我们最终得到准确的启动时间统计数据以及启动优化的⼀些头绪。具体优化的⽅法参加下⼀篇⽂章《如何优化Androd App启动速度》。- 参考【1】单⼑⼟⾖,d 开发之 App 启动时间统计【2】Android Developer,Launch-Time Performance【3】./multi_core_dump,d Application Launch【4】./multi_core_dump,d Application Launch Part 2【5】罗升阳,d系统源代码情景分析【6】Colt McAnlis,d Performance Patterns Season 6
发布者:admin,转转请注明出处:http://www.yc00.com/web/1687840346a49853.html
评论列表(0条)