统计启动时间
adb
查询
adb shell am start -W packageName/targetActivity
不适合线上,且时间非准备。适合分析竞品
手动打点
定一个辅助类,记录开始和结束的时间,其实时间差就是启动时间。
1 | class LaunchTimeTester { |
在Application的attachBaseContext
函数记录启动时间。
1 | override fun attachBaseContext(base: Context?) { |
而结束时间,建议是在数据拉取后结束统计,而不是在onWindowFocusChanged
首帧结束统计,例如在启动页广告加载出来的时候计算启动的结束时间。
1 | ivBottomLogo.viewTreeObserver.addOnPreDrawListener(object : OnPreDrawListener1 { |
LaunchTimeTester.getLaunchTime()
就输出了打印时间。
时间准确且适合线上,是比较推荐的统计方式。
工具
1、traceview
traceview
手动埋点,精准。但是运行时开销严重,整体变慢,可能带偏优化方向。
图形显示展示执行时间,调用栈等,信息比较全面,包含所有线程。
流程 :
1 | //在要统计信息的开始地方 |
然后运行程序,会在/sdcard/Android/data/packageName/files
目录下,生成.trace
后缀的文件。
双击该文件后,Android Studio
会展示相关信息
左边展示了线程数和线程名,例如上图有20条线程,有main
、bugly
等线程。
主要看下右边的Summary
,TopDown
、FlameChart
、BottomUp
。单击左边线程名,会在右边显示单击线程的相关信息,例如上图的线程名main
下图是TopDown
,BottomUp
与它的区别就子方法的展示是倒过来的。其中要注意的是右上角的WallClockTime
和ThreadTime
概念。ThreadTime
表示该方法代码所执行的时间,而WallClockTime
表示CPU花在该方法的时间,总是大于ThreadTime
,也是我们分析的目标。
FlameChart
主要把相同方法的调用统计在一起,即一个方法被调用多次的统计信息。
traceview
的代码统计不应该存在线上环境,而适合在分析的时候,由于统计信息很全面,也很耗时,占用资源。
2、systrace
默认情况下,systrace
只收集系统信息,所以需要在代码添加下面代码来收集APP的引用信息。
1 | //开始的地方 |
systrace
轻量级,开销小,直观反映CPU的利用率。cputime
代码消耗cpu
的时间(重点指标)与walltime
代码执行时间
启动耗时分析
通过分析各部分,找出耗时的地方,然后针对性优化。
1、手动埋点
即通过第一步手动打点,记录各部分用时。侵入性比较强,影响原有代码布局;工作量大,需要填写大量的启动时间。
2、AOP切面埋点
优点无侵入性代码,修改方便。Android
上使用AspectJ
。
**Join Points
**程序运行时的执行点,可以作为切面的地方:函数调用、执行;获取和设置变量;类初始化。
**PointCut
**带条件的Join Points
,对Join Points
进行筛选。
**Advice
**一种Hook,要插入代码的位置。即Before
和 After
注解。
优化
1、优化白屏和黑屏
添加自定义背景,避免直接显示白屏和黑屏
2、异步优化
使用子线程分担主线程任务,并发减少时间。根据CPU核心数量,创建合适的线程数量的线程池。
优化方案
1、延时初始化
延时初始化可以理解为日常上下班高峰期错峰出行的原理。本来大家都挤在Application
的onCreate
函数中,而且是希望越早越好。所以都会竞争资源,造成资源竞争,CPU会切线程等。所以就会出现启动慢的情况。这时如果将一些不紧急的第三方SDK(例如Bugly
)通过Handler.postDelay
进行延时初始化,能起到一定的优化效果。但缺点也明显,只能管控延时时间,如果刚好在延迟时间到期,APP也很繁忙,同样也会造成卡顿。
2、闲时初始化
所谓闲时,就是相关资源不紧张,例如CPU、内存等。更细的说,当前消息队列没有信息,处于空闲状态。阅读过Handler
机制源码应该知道在MessageQueue
的next
函数,在消息队列没有消息时会调用IdelHandler
处理不紧急消息。通过IdelHandler我们不仅可以处理一些第三方SDK初始,也可以初始化我们的一些闲时动作,例如异常信息上传,用户Token上传等。
代码示例:
1 | public class DelayDispatcher { |
使用:
1 | DelayDispatcher().addTask { |
通过这个初始化方案,Application
的onCreate
函数大概执行时间大概减少了一半,原先在1113毫秒左右,使用在452毫秒左右。推荐使用。