1App启动优化介绍
背景介绍
第一体验 :八秒定律
启动分类
1冷启动
耗时最多、衡量标准
帮助寻找优化方向
2热启动
最快
后台--》前台
3温启动
较快
重走LifeCycle 而不会重走Application、进程等的创建
相关任务
冷启动之前:
启动APP
加载空白Window
创建进程
随后任务:
创建Application
启动主线程
创建MainActivity
加载布局
布置屏幕
首帧绘制
优化方向
Application和Activity生命周期阶段
2App启动时间测量方式
adb命令
adb shell am start -W packagename/packagename.首屏Activity
2.png
ThisTime:最后Activity启动耗时
TotalTime:所有Activity启动耗时
WaitTime:Ams启动Activity总耗时
线下方便,不能带到线上;
非严谨、精确
手动打点
启动时埋点,结束时埋点,二者差值
3.png开始计时:
在“attachBaseContext()”里调用!!
结束计时:
误区:onWindowFocusChanged只是首帧时间,首次绘制
正确:真实数据展示,数据资料绘制的第一条展示。
精确,可带到线上,推荐使用。
避开误区,采用第一条数据展示
addOnDrawListener要求API16 ,替换为addPreDrawListener。
3App启动优化工具选择
traceview、systrace 两种方式互相补充
1traceview
图形的形式展示执行时间、调用栈等
信息全面,包含所有线程
使用方式
//开始
Debug.startMethodTracing("文件名1")
//结束
Debug.stopMethodTracing()
生成文件在SD卡:Android/data/packagename/files/文件名1.trace
traceview其实默认有一个最大限制8M,可在方法里参数指定。
文件名1.trace 打开
Top Down:
函数的调用表,逐级调用
cpu真正花在上面的时间
Call Chart:
7.pngBottom UP 是Top Down 的反向即谁调用了我。
另一个Flame不常用
总结:
运行时开销严重,整体都会变慢
可能会带偏优化方向。
traceview (可在代码中埋点)
2systrace
结合Android 内核的数据生成Html报告
API18以上,之下推荐TraceCompat
使用方式
8.png起点:
9.png结束:
10.png
[图片上传中...(12.png-7eb89d-1615694063053-0)]
11.png 12.png 13.png 14.png总结:
轻量级,开销小 ,埋在哪做哪,TraceView都做
直接反映CPU利用率
CPUtime与walltime区别:我们要优化的是CPUtime,是CPU真正花在上面的时间
15.png为什么二者时间不一样:锁冲突。
4优雅获取耗时
常规方式
背景:需要知道启动阶段所有方法耗时
实现:手动埋点
统计每个方法耗时的话会非常丑陋,会导致强耦合。
侵入性强
工作量大
*SystemClock.curretThreadMillis();就是CPU时间
AOP(Aspect Oriented Programming)面向切面编程 介绍
针对同一类问题的统一处理:性能问题
无需入侵代码
AspectJ使用
18.pngAOP相关知识点
19.png 20.png 21.png 22.png 23.pngAOP实战
24.png 25.png修改一下代码:
26.png 27.png我们没有在原来代码上操作,只是加入了一个类,非常优雅。
无侵入性;
修改方便;
5异步优化详解
优化技巧
Theme 切换(首屏、闪屏):感官上的快
drawable下创建文件:
28.png 29.png异步优化实战
核心思想:子线程分担主线程任务,并行减少时间
主线程一个在工作,手机为多核。有的厂家只分配一个。
使用线程池:
启动阶段最好不做new线程操作:1粗放;2可能导致内存泄漏;3不能复用
参考AsyncTask获取CPU数量
private final int CPU_COUNT=Runtime.getRuntine().availableProcessors();
private final int CORE_POOL_SIZE = Math.max(2,Math.min(CPU_COUNT-1),4);
ExecutorService service =Executors.newFixedThreadPool(CORE_POOL_SIZE );
service.submin(new Runnable(){
@Override
public void run(){
//Bugly
initBugly();
}
});
service.submin(new Runnable(){
@Override
public void run(){
//友盟
initUmeng();
}
});
....
*可不可以都放在一个Runnable里呢?
可以,但是不好。如果比如new了三个线程只用了一个,等于资源浪费。
*可不可以方法都用子线程呢?
不可以。
代码并不满足异步需求。此部分必须放在 主线程中。
代码有先后顺序的,
解决办法CountDownLatch:
private CountDownLatch mCountDownLatch =new CountDownLatch(1);//满足一次
onCreate(){
service.submin(new Runnable(){
@Override
public void run(){
//友盟
initUmeng();
mCountDownLatch.countDown();//满足了一次
}
});
.......
//Create结束时调用
try{
mCountDownLatch.await();//检测条件,如果次数不够就在此等待
}catch(InterruptedException e){
e.printStackTrace();
}
}
30.png
6异步初始化最优解---启动器
常规异步痛点
31.png1代码不优雅;
2存在依赖关系的不好处理。虽然可以像下面这样放一起:
3不方便统计。维护成本高。
启动器介绍
核心思想:充分利用CPU多核,自动梳理启动顺序。
1代码Task化,启动逻辑抽象成Task。
2根据所有任务依赖关系排序生成一个有向无环图。
3多线程按照排序后的优先级依次执行。
启动器实战
34.png 35.png 36.png 37.png7更优的初始化延迟方案
常规方案
New Handler().postDelayed
Feed显示之后调用
时机不便控制
主线程会卡,Feed卡顿,马上使用的话会发现。
不够优雅,可维护性差
更优方案 延迟方案
核心思想,对延迟任务进行分批初始化
利用IdleHandler特性,空闲执行
38.png 39.png8启动优化其他方案
优化总方针
异步、延迟、懒加载
技术、业务相结合
注意事项
wall time与Cpu time
40.png监控的完善
41.png收敛启动修改代码权限
其他方案
提前加载SharedPreferences:
1Multidex之前加载,利用此阶段的CPU。
2复写getApplicationContext()返回this
启动阶段不启动子进程
子进程会共享CPU资源,导致主进程CPU紧张
注意启动顺序:APP onCreate 之前是ContentProvider
类加载优化:提前异步类加载
Class.forName()之家在类本身及其静态变量的应用类
new 类 可以额外加载类成员变量的引用类
其他
启动阶段抑制GC
CPU锁频(提一句,可能导致耗电量增加)
网友评论