文章目录
一、性能优化的难点
1. 性能表现差
- APP启动慢、卡顿、丢帧
- 内存占用高、抖动频繁
- 耗电、网络请求慢
- 崩溃率、异常率高
2. 线上问题无从追查
- 如何保证异常感知灵敏度
- 如何复原“案发现场”
- 如何快速“止血”成功
3. 性能优化的长期开销大
- 如何扼杀问题于萌芽
二、启动速度优化
- 冷启动
- 耗时最多,是启动速度的衡量标准
Click Event
-IPC
-Process.start
-ActivityThread
-bindApplication
-LifeCycle
-ViewRootImpl
- 热启动
- 最快
后台
-前台
- 温启动
- 较快
LifeCycle
冷启动之前:
- 启动APP
- 加载空白Window
- 创建进程
随后:
- 创建Application
- 启动主线程
- 创建MainActivity
- 加载布局
- 布置屏幕
- 首帧绘制
优化方向:
Application和Activity生命周期
1. 启动时间的测量方式
(1)adb
adb shell am start -W packageName/packageName.SplashActivity
输出说明:
totalTime
: 所有Activity启动耗时;
ThisTime
: 最后一个Activity启动耗时;
WaitTime
: AMS启动Activity的总耗时。
特点:
- 线下使用方便,不能带到线上
- 非严谨精确的时间
(2)手动打点
启动时候打点,结束时候计算
在Application的attachBaseContext(Context cntext)
中记录初始计时
public class LaunchTimer {
private static long sStartTime;
/**
* 开始计算
* 在Application的attachBaseContext方法调用
*/
public static void startRecord(){
sStartTime = System.currentTimeMillis();
}
/**
* 结束计时
* 在界面显示成功的时候计算
* ViewTreeObserver viewTreeObserver = mView.getViewTreeObserver();
* viewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
* @Override
* public boolean onPreDraw() {
* viewTreeObserver.removeOnPreDrawListener(this);
* finishRecord();
* return true;
* }
* });
*/
public static void finishRecord() {
long time = System.currentTimeMillis() - sStartTime;
Log.d("启动耗时", time + "");
}
}
2. 启动优化工具的选择
(1)traceview
a. 使用方式
Debug.startMethodTracing("");
Debug.stopMethodTracing();
生成本地文件路径:Androdi/data/packagename/files
b. 特点
- 运行时开销严重,整体都会变慢
- 由于会导致整体变慢,所以可能带偏优化方向
- traceview与cpu profiler
(2)systrace
- 结合Android内核的数据,生成Html报告
- API18以上才可使用,推荐使用TraceCompat
a.使用方式
python systrace.py -t 10 [other-options][categories]
b.特点
- 轻量级,开销小
- 直观反映CPU利用率
c.cputime与walltime的区别
- walltime是代码执行时间
- cputme是代码小号cpu的时间(重点指标)
- 举例:锁冲突
3. 获取方法耗时
(1)常规方法
代码插入System.currentTimeMills()
特点
- 侵入性强
- 工作量大
(2)AOP
面向切面编程
a. 特点
- 针对同一类问题的统一处理
- 无侵入添加代码
AspectJ使用
https://www.jianshu.com/p/f577aec99e17
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0'
apply plugin: 'android-aspectjx'
implementation 'org.aspectj:aspectjrt:1.8.13'
b. 名词解释
- Join Points
程序运行时的执行点,可以作为切面的地方
- 方法调用、执行
- 获取、设置变量
- 类初始化
- PointCut
带条件的JoinPoints - Advice
一种Hook,要插入代码的位置
- Before:PointCut之前执行
- After:PointCut之后执行
- Around:PointCut之前、之后分别执行
- 语法
@Before("execution(*android.app.Activity.on**(..))")
public void onActivityCalled(JoinPoint joinPoint) throw Throwable {
}
- Before:Advice,具体插入位置
- execution:处理Join Point的类型,call、execution
- (android。app.Activity.on*(…)):匹配规则
- onActivityCalled:要插入的代码
c. 使用
- 类上添加注解
@Aspect
- 方法添加注解
@Around(call(* com.example.app.TestActivity.**(..)))
等
public void getDuration(ProceedingJoinPoint point) {
Signature signature = point.getSignature();
String methodName = signature.toShorString();
long time = System.currentTmeMillis();
point.proceed();
Log.d("耗时", methodName + " " + (System.currentTmeMillis() - time));
}
三、异步优化
1. 技巧
Theme切换(感觉上会快)
- drawable中创建文件
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:opacity="opaque">
<item android:drawable="@android:color/white" />
<item>
<bitmap
android:gravity="bottom"
android:src="@drawable/first_ic_logo" />
</item>
</layer-list>
<style name="AppTheme.Launcher">
<item name="android:background">@drawable/first_layer</item>
</style>
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
setTheme(R.style.MyTheme);
super.onCreate(savedInstanceState);
}
2. 核心思想
子线程分担主线程任务,并行减少时间
int processors = Runtime.getRuntime().availableProcessors();
int count = Math.max(2, Math.min(processors - 1, 4));
ExecutorService executorService = Executors.newFixedThreadPool(count);
executorService.submit(new Runnable() {
@Override
public void run() {
//todo:需要在子线程执行的方法
countDownLatch.countDown();
}
});
//该int值表示要执行多少次countDown方法,只有执行完对应次数之后,才不会await,表示要等子线程的方法执行完
CountDownLatch countDownLatch = new CountDownLatch(int次数);`
countDownLatch.countDown();
countDownLatch.await();
表示,执行了int次的countDown方法之后,就不再await方法
注意:
- 不符合异步的要求
- 需要在某些阶段完成(CountDownLatch )
- 区分CPU密集型和IO密集型任务
缺点:
- 代码不优雅
- 场景不好处理(如这个方法需要上个方法的结果)
- 维护成本高
3. 启动器
异步初始化的最优解
充分利用CPU多核,自动梳理任务顺序
流程:
- 代码Task化,启动逻辑抽象成Task
- 根据所有任务依赖关系生成一个有向无环图
- 多线程按照排序后的优先级依次执行
代码见【3-10 异步初始化最优解-启动器-2】
4. 延迟初始化
对延迟任务进行分批初始化
可以提升用户体验
利用IdleHandler特性,在空闲的时候执行
【3-11 更优秀的延迟初始化方案】
5. 其他方案
- 启动阶段抑制GC
- CPU锁频(会增加耗电量)
6. 其他
- 提前异步处理SharedPreferences
- 启动阶段不启动子进程
- 提前异步类加载