App Launch Time Measurement
2017-03-01 20:16
253 查看
本文记录下分析应用启动时间的总结。
关于应用启动时间测量的分析已经有不少不错的文章做了总结,下面是比较好的几篇:
1.Android性能优化典范-第6季
2.测量Activity 的启动时间
3.Activity到底是什么时候显示到屏幕上的呢
上面的每篇都各有特色,我这篇也只是在他们的分析上记录下自己学习和研究过程的总结。
从Android KitKat版本开始,Logcat中会输出从程序启动到Activity显示到屏幕上所花费的时间,这个时间包含了进程启动的时间,比较适合测量程序的启动时间。
上面信息的打印来自
drawn time类似,我们在下面会介绍它的详细实现过程。
除了看Logcat之外,我们还有其他的方式来查看上面的时间,例如使用
或者查看EventLog的方法来查看
通常应用启动的时候都会以异步加载的方式来加快应用的启动速度,但是上面的display time是不包含异步加载所耗费的时间,所以为了准确衡量应用的启动时间,我们可以在异步加载完毕之后调用
查看方式和输出结果类似上面的查看display time的过程
下面是
其中的
上面代码中有个起始时间(
注:下面代码中的
那
关于应用启动时间测量的分析已经有不少不错的文章做了总结,下面是比较好的几篇:
1.Android性能优化典范-第6季
2.测量Activity 的启动时间
3.Activity到底是什么时候显示到屏幕上的呢
上面的每篇都各有特色,我这篇也只是在他们的分析上记录下自己学习和研究过程的总结。
1.查看display time
从Android KitKat版本开始,Logcat中会输出从程序启动到Activity显示到屏幕上所花费的时间,这个时间包含了进程启动的时间,比较适合测量程序的启动时间。I ActivityManager: Displayed com.meizu.flyme.applaunch/.MainActivity: +379ms //厂商定制过的OS可能会有些不同,例如FlymeOS中的输出 I ActivityManager: [AppLaunch] Displayed Displayed com.meizu.flyme.applaunch/.MainActivity: +480ms
上面信息的打印来自
ActivityRecord类的
reportLaunchTimeLocked方法,它的实现如下所示,整个过程和下面的fully
drawn time类似,我们在下面会介绍它的详细实现过程。
除了看Logcat之外,我们还有其他的方式来查看上面的时间,例如使用
am start的方式查看
TotalTime
$ adb shell am start -W com.meizu.flyme.applaunch/.MainActivity Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.meizu.flyme.applaunch/.MainActivity } Status: ok Activity: com.meizu.flyme.applaunch/.MainActivity ThisTime: 479 TotalTime: 479 WaitTime: 499 Complete
或者查看EventLog的方法来查看
am_activity_launch_time
$adb shell logcat -b events ... I am_activity_launch_time: [0,200792421,com.meizu.flyme.applaunch/.MainActivity,478,478]
2.查看fully drawn time
通常应用启动的时候都会以异步加载的方式来加快应用的启动速度,但是上面的display time是不包含异步加载所耗费的时间,所以为了准确衡量应用的启动时间,我们可以在异步加载完毕之后调用Activity.reportFullyDrawn()方法来告诉系统加载完成,以便获取整个应用启动的耗时。
查看方式和输出结果类似上面的查看display time的过程
//Logcat中的输出 I ActivityManager: Fully drawn com.meizu.flyme.applaunch/.MainActivity: +2s319ms //EventLog中的输出 ... I am_activity_fully_drawn_time: [0,200792421,com.meizu.flyme.applaunch/.MainActivity,478,478]
下面是
Activity.reportFullyDrawn()方法的实现,从注释来看,这个方法主要是用来帮助我们测量应用的启动时间,因为系统最多只能确定应用的window第一次绘制和显示的时间点,不能确定应用真正加载完成处于可以使用状态的时间点,所以需要开发者来显式调用这个方法以通知系统应用已经启动完毕可以使用了。
/** * Report to the system that your app is now fully drawn, purely for diagnostic * purposes (calling it does not impact the visible behavior of the activity). * This is only used to help instrument application launch times, so that the * app can report when it is fully in a usable state; without this, the only thing * the system itself can determine is the point at which the activity's window * is <em>first</em> drawn and displayed. To participate in app launch time * measurement, you should always call this method after first launch (when * {@link #onCreate(android.os.Bundle)} is called), at the point where you have * entirely drawn your UI and populated with all of the significant data. You * can safely call this method any time after first launch as well, in which case * it will simply be ignored. */ public void reportFullyDrawn() { if (mDoReportFullyDrawn) { mDoReportFullyDrawn = false; try { ActivityManagerNative.getDefault().reportActivityFullyDrawn(mToken); } catch (RemoteException e) { } } }
其中的
ActivityManagerNative的
reportActivityFullyDrawn方法会经过Binder调用到AMS的
reportActivityFullyDrawn方法,最终会调用到
ActivityRecord的
reportFullyDrawnLocked方法,内容与
reportLaunchTimeLocked方法类似。
public void reportFullyDrawnLocked() { final long curTime = SystemClock.uptimeMillis(); if (displayStartTime != 0) { reportLaunchTimeLocked(curTime); } final ActivityStack stack = task.stack; if (fullyDrawnStartTime != 0 && stack != null) { final long thisTime = curTime - fullyDrawnStartTime; final long totalTime = stack.mFullyDrawnStartTime != 0 ? (curTime - stack.mFullyDrawnStartTime) : thisTime; if (SHOW_ACTIVITY_START_TIME) { Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0); EventLog.writeEvent(EventLogTags.AM_ACTIVITY_FULLY_DRAWN_TIME, userId, System.identityHashCode(this), shortComponentName, thisTime, totalTime);//EventLog中的输出 StringBuilder sb = service.mStringBuilder; sb.setLength(0); sb.append("Fully drawn "); sb.append(shortComponentName); sb.append(": "); TimeUtils.formatDuration(thisTime, sb); if (thisTime != totalTime) { sb.append(" (total "); TimeUtils.formatDuration(totalTime, sb); sb.append(")"); } Log.i(TAG, sb.toString());//Logcat中的输出 } if (totalTime > 0) { //service.mUsageStatsService.noteFullyDrawnTime(realActivity, (int) totalTime); } stack.mFullyDrawnStartTime = 0; } fullyDrawnStartTime = 0; }
上面代码中有个起始时间(
fullyDrawnStartTime),它是在哪里设置的呢?它是在
ActivityStack的
setLaunchTime方法中设置的。
注:下面代码中的
Trace.asyncTraceBegin和
Trace.asyncTraceEnd实际上会调用到系统中
atrace的
async_start和
async_stop(可以通过
adb shell atrace -h查看到这两个命令的选项)。
void setLaunchTime(ActivityRecord r) { if (r.displayStartTime == 0) { r.fullyDrawnStartTime = r.displayStartTime = SystemClock.uptimeMillis(); if (mLaunchStartTime == 0) { startLaunchTraces(r.packageName); mLaunchStartTime = mFullyDrawnStartTime = r.displayStartTime; } } else if (mLaunchStartTime == 0) { startLaunchTraces(r.packageName); mLaunchStartTime = mFullyDrawnStartTime = SystemClock.uptimeMillis(); } } private void startLaunchTraces(String packageName) { if (mFullyDrawnStartTime != 0) { Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0); } Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: " + packageName, 0); Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0); } private void stopFullyDrawnTraceIfNeeded() { if (mFullyDrawnStartTime != 0 && mLaunchStartTime == 0) { Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0); mFullyDrawnStartTime = 0; } }
那
setLaunchTime方法是何时调用的呢?它是在
ActivityStackSupervisor.startSpecificActivityLocked方法中调用的!
startSpecificActivityLocked方法中会判断应用进程是否启动了,如果没有启动就调用
startProcessLocked方法来启动进程,内部会调用
Process类的
start方法来启动新进程;否则调用
realStartActivityLocked方法继续执行,这个方法会调用
scheduleLaunchActivity方法,内部将会调用
Activity的
onCreate方法,开始Activity的生命周期。
void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) { // Is this activity's application already running? ProcessRecord app = mService.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid, true); r.task.stack.setLaunchTime(r);//在这里设置launch start time if (app != null && app.thread != null) { try { if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0 || !"android".equals(r.info.packageName)) { // Don't add this if it is a platform component that is marked // to run in multiple processes, because this is actually // part of the framework so doesn't make sense to track as a // separate apk in the process. app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode, mService.mProcessStats); } realStartActivityLocked(r, app, andResume, checkConfig); return; } catch (RemoteException e) { Slog.w(TAG, "Exception when starting activity " + r.intent.getComponent().flattenToShortString(), e); } // If a dead object exception was thrown -- fall through to restart the application. } mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0, "activity", r.intent.getComponent(), false, false, true); }
相关文章推荐
- App 不能启动错误:“failed to launch in time”
- App Launch time & Activity creation (Android Performance Patterns Season 6 Ep. 2)
- process launch failed:time out trying to launch app
- App Launch time 101 (Android Performance Patterns Season 6 Ep. 1)
- Measuring App launch time
- How Can I Launch The Appstore App Directly from my Application
- installshield 制作安装包中执行其他程序LaunchAppAndWait的简介
- How to:如何在调用外部文件时调试文件路径(常见于使用LaunchAppAndWait和LaunchApp函数)
- 如何使用InstallShield中的LaunchAppAndWait()
- 设置APP的启动图片(Launch Image)
- 获取APP的Launch Image
- xcode5 AppIcon LaunchImage 尺寸大小
- iOS(一)UI的appIcon和BrandAsset(LaunchImage)
- app性能优化之Time Profiler工具的使用
- Xcode运行时错误:Could not launch "*.app"--No such file or directory(Users/*/Library/...)
- Ext.app.Controller.onLaunch方法介绍。
- How to launch App Store from my iPhone application?
- novatar-webapp.launch.tpl
- Xcode —— waiting for (app's name) to launch 模拟器开不了
- (adhoc) process launch failed: timed out waiting for app to launch