App性能优化:启动优化篇
本篇主要探讨App启动优化相关内容
1、启动分类
- 冷启动
- 热启动
- 温启动
冷启动
1.冷启动流程图
热启动
最快:后台切换到前台
温启动
较快:重新执行Activity的生命周期,不会重新创建Application
冷启动相关任务
冷启动之前:
- 启动App
- 加载空白Window
- 创建进程
随后任务:
- 创建Application
- 启动主线程
- 创建MainActivity
- 加载布局
- 布置屏幕
- 首帧绘制
优化方向:
在Application和Activity生命周期阶段,是我们能比较好干预的阶段,也是优化的重点阶段
2、启动时间的测量
- Adb命令的方式
Adb shell am start -W packagename/packagename.首屏Activity
效果图如下:
thisTime:最后一个Activity启动耗时
totalTime:所有Activity启动耗时
waitTime:AMS启动Activity的总耗时
手动打点
误区:OnWindowFocusChanged只是首帧时间
正解:真实数据展示的时间,列表第一条数据展示出来:
btn_start.viewTreeObserver.addOnDrawListener(object:ViewTreeObserver.OnDrawListener{
override fun onDraw() {
val endTime=System.currentTimeMillis()
}
})
3、启动优化工具的使用
一、TraceView
二、SysTrace
TraceView 工具
TraceView的特点:
- 图形的形式展示执行时间、调用栈
- 信息全面,包含所有线程
TraceView的使用的使用:
- Debug.startMethodTracing(“App”);
- Debug.stopMethodTracing();
- 生成文件的位置:以Android 9 模拟器为例,文件位于:
mnt/sdcard/Android/data/packagename/files/App.trace
打开.trace后,Android studio 会通过Profiler分析文件
生成cpu调用的图形信息,左侧会显示各个线程我们选中main线程
下面就是main线程在时间范围00.000-00.298之间的图表
-
Call Chart
水平轴表示函数调用(或调用方)的时间,并沿垂直轴显示其被调用者。 对系统 API 的函数调用显示为橙色,对应用自有函数的调用显示为绿色,对第三方 API(包括 Java 语言 API)的函数调用显示为蓝色。 下图展示了一个调用图表示例,并描绘了给定函数的 Self time、Children time 以及总时间的概念。
相关函数可以右键Jump to source
-
Flame Chart
Flame Chart 标签提供一个倒置的调用图表,其中水平轴不再代表时间线,它表示每个函数相对的执行时间。
-
Top Down
Top Down 标签显示一个函数调用列表,在该列表中展开函数节点会显示函数调用的子函数
traceview注意事项
- 运行时开销很大,整体都会变慢
- 可能会带偏优化方向
- traceview与cpu Profiler,老版本使用traceView,新版本就是用Profiler打开的trace文件,Profiler更加强大,同时可以做内存,网络等更多的更多的性能监控。
- traceView可以进行手动打点,进行更加精确的监控
- wall clock time和Thread Time,wall time是占用的cpu的时间,thread time是线程执行的时间,cpu时间是小于线程时间的。
- .trace文件默认最大是8M的大小,如果初始化任务较多,最好自定义文件大小,方便精确监控
systrace工具的使用
参考官网:systrace官方指南
- systrace:结合android内核的数据,生成Html报告
- API 18以上使用,推荐使用TraceCompat
- systrace 是一段python脚本,通过终端执行命令:首先进入strace.py所在目录,mac在Users/username/Library/Android/sdk/platform-tools/systrace目录下,然后运行python命令:
python systrace.py -b 10240 -t 5 -a 包名 -o hdperformance.html sched gfx view wm am app- -b:指定buffer大小
- -t:指定时间 5s
- -a:指定包名
- -o:指定输出文件的名称
systrace监控启动时间
1、 在application的oncreate里面添加代码:
TraceCompat.beginSection("appOnCreate");
//初始化操作
...
TraceCompat.endSection();
2、通过命令行开启systrace
执行命令后打开app,即可监听到启动信息,会在systrace目录下生成一个*.html文件。通过浏览器打开hdperformance.html文件
trace.html文件分析
-
在左侧根据包名找到我们的进程
-
在左侧点击Frames下面的UI Thread
-
操作快捷键WSAD,放大缩小,左右移动,找到在代码中设置的tag:appOnCreate
-
点击tag,在点击M选中这一部分
-
选中后可以看到cpu时间和线程时间
-
上面可以看到CPU 各个核心的使用情况
systrace特点
- 轻量级,开销小
- 直观反应cpu利用率
- cpu time 和wall time的区别:cputime小于wall time,我们真正需要关注的优化点在cpu time
4、优雅获取方法耗时
AspectJ 面向切面编程
- classpath ‘com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0’
- implementation ‘org.aspectj:aspectjrt:1.8.+’
- apply plugin :‘android-aspectjx’
JointPoint
- 程序运行时候的执行点
- 获取、设置变量
- 类初始化
Advice
- 一种Hook,要插入代码的位置
- Before:PointCut之前执行
- After:PointCut之后执行
- Around:PointCut之前之后分别执行
AspectJ 使用实例
配置好上面的aspectj插件后,编写如下aspectj代码,即可监听application启动时候每个方法的耗时:
@Aspect
public class PerformanceAop {
@Around("call(* packagename.ApplicationContext.**(..))")
public void getTime(ProceedingJoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
String name = signature.getName();
long time = System.currentTimeMillis();
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
Log.d("PerformanceAop", name + "__cost=" + (System.currentTimeMillis() - time));
}
}
执行效果如下:
AOP的优点
- 无侵入性
- 修改方便
5、异步优化最优解-启动器
核心思想:子线程分担主线程任务,并行减少时间
异步优化痛点
- 代码不优雅
- 场景不好处理(依赖关系)
- 维护成本高
启动器核心思想
充分利用CPU多核,自动梳理任务顺序
- 任务Task 化,启动逻辑抽象为Task
- 根据所有任务依赖关系排序生成一个有向无环图
- 多线程按照排序后的优先级依次执行
更优秀的延迟初始化方案-分批初始化
- 利用IdleHandler特性,空闲执行
启动优化其他方案
- 提前异步SharedPreferences,在MutilDex前
- 启动阶段不使用子进程
- 提前异步类加载(mutilDex install 之后直接开启线程,通过class.forName或者new Class)