1App启动优化介绍
背景介绍
第一体验 :八秒定律
启动分类
1冷启动
耗时最多、衡量标准
1.png
帮助寻找优化方向
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只是首帧时间,首次绘制
正确:真实数据展示,数据资料绘制的第一条展示。
4.png
精确,可带到线上,推荐使用。
避开误区,采用第一条数据展示
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:
函数的调用表,逐级调用
5.png
6.png
cpu真正花在上面的时间
Call Chart:
7.png
Bottom 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优雅获取耗时
常规方式
背景:需要知道启动阶段所有方法耗时
实现:手动埋点
16.png
17.png
统计每个方法耗时的话会非常丑陋,会导致强耦合。
侵入性强
工作量大
*SystemClock.curretThreadMillis();就是CPU时间
AOP(Aspect Oriented Programming)面向切面编程 介绍
针对同一类问题的统一处理:性能问题
无需入侵代码
AspectJ使用
18.png
AOP相关知识点
19.png
20.png
21.png
22.png
23.png
AOP实战
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.png
1代码不优雅;
2存在依赖关系的不好处理。虽然可以像下面这样放一起:
32.png
3不方便统计。维护成本高。
启动器介绍
核心思想:充分利用CPU多核,自动梳理启动顺序。
1代码Task化,启动逻辑抽象成Task。
2根据所有任务依赖关系排序生成一个有向无环图。
3多线程按照排序后的优先级依次执行。
33.png
启动器实战
34.png
35.png
36.png
37.png
7更优的初始化延迟方案
常规方案
New Handler().postDelayed
Feed显示之后调用
时机不便控制
主线程会卡,Feed卡顿,马上使用的话会发现。
不够优雅,可维护性差
更优方案 延迟方案
核心思想,对延迟任务进行分批初始化
利用IdleHandler特性,空闲执行
38.png
39.png
8启动优化其他方案
优化总方针
异步、延迟、懒加载
技术、业务相结合
注意事项
wall time与Cpu time
40.png
监控的完善
41.png
收敛启动修改代码权限
其他方案
提前加载SharedPreferences:
1Multidex之前加载,利用此阶段的CPU。
2复写getApplicationContext()返回this
启动阶段不启动子进程
子进程会共享CPU资源,导致主进程CPU紧张
注意启动顺序:APP onCreate 之前是ContentProvider
类加载优化:提前异步类加载
Class.forName()之家在类本身及其静态变量的应用类
new 类 可以额外加载类成员变量的引用类
其他
启动阶段抑制GC
CPU锁频(提一句,可能导致耗电量增加)











网友评论