性能优化(一):启动优化

724 阅读5分钟

一.App启动优化简介

1.背景介绍:
 1).第一体验;
 2).八秒定律(用户在8秒时间未打开应用,70%的用户会放弃等待);
2.启动分类:
 App startup time (官方文章)
 1).冷启动:耗时最多,衡量标准
 Click Event->IPC->Process.start->ActivityThread->bindApplication->LifeCycle->ViewRootImpl
 2).热启动:最快 后台->前台
 3).温启动:较快 重走Activity的生命周期;

冷启动相关任务:
1.启动App;
2.加载空白Window;
3.创建进程;
4.创建Application;
5.启动主线程;
6.创建MainActivity;
7.加载布局;
8.布置屏幕;
9.首帧绘制;

优化方向:Application和Activity生命周期

二.启动时间测量方式

1.adb命令:adb shell am start -W packagename/首屏Activity

微信图片_20210504153430.png

 ThisTime:最后一个Activity启动耗时;
 TotalTime:所有Activity启动耗时;
 WaitTime:AMS启动Activity的总耗时;
 特点:
  1.线下使用方便,不能带到线上;
  2.非严谨,精确的时间;
2.手动打点:
 启动时埋点,启动结束埋点,二者差值
 误区:onWindowFocusChanged只是Activity首帧时间,并不代表界面已经展示出来;
 正解:真实数据展示出来,Feed第一条展示,首帧时间与真实数据展示出来还有一段距离;
 起点:Application->attachBaseContext();
 终点:真实的第一条数据展示出来时打点;

特点:精确,可带到线上,推荐使用

三.启动优化工具选择

1.traceView
 1).图形的形式展示执行时间,调用栈等;
 2).信息全面,包含所有线程;
 使用方式:
 1.Debug.startMethodTracing("文件名");
 2.Debug.stopMethodTracing();
 3.生成文件在sd卡:Android/data/packagename/files
 特点:
 1.运行时开销严重,整体都会变慢;
 2.可能会带偏优化方向;
 3.traceview与cpu profiler
2.systrace
 特点:
 1.轻量级,开销小;
 2.直观反映CPU利用率;
 3.walltime与cputime区别:
  walltime:代码执行时间;
  cputime:代码消耗cpu的时间(重点指标);
  举例:锁冲突

四.如何优雅获取方法耗时

背景:需要知道启动阶段所有方法耗时
实现:手动埋点
常规实现:侵入性强,工作量大;
AOP(面向切面编程)实现:
特点
1.针对同一类问题的统一处理;
2.无侵入添加代码;
3.修改方便;
Join points:
程序运行时的执行点,可以作为切面的地方:
1.函数调用,执行;
2.获取,设置变量;
3.类初始化;
PointCut:带条件的JoinPoints
Advice:
一种Hook,要插入代码的位置;
1.Before:PointCut之前执行;
2.After:PointCut之后执行;
3.Around:PointCut之前之后分别执行;  

五.异步优化详解

1.Theme优化小技巧
 Theme切换:感觉上快,利用空白Window;

微信图片_20210505093841.png 微信图片_20210505094034.png

2.异步优化
 核心思想:子线程分担主线程任务,并行减少时间

微信图片_20210505094405.png

注意事项:
 1).不符合异步要求;
 2).需要在某阶段完成;
 3).区分CPU密集型和IO密集型任务;
3.异步优化最优解:
 1).常规异步痛点:
  a.代码不优雅;
  b.场景不好处理(依赖关系);
  c.维护成本高;
2).启动器(重点)
 启动器流程:
 1.代码Task化,启动逻辑抽象为Task;
 2.根据所有任务依赖关系排序生成一个有向无环图;
 3.多线程按照排序后的优先级依次执行;
 4.解决必须在主线程执行及有依赖关系的task;

微信图片_20210505100130.png

4.延迟初始化方案
 常规初始化痛点:
 a.时机不便控制;
 b.导致Feed卡顿;
 更优方案:
 核心思想:对延迟任务进行分批初始化
 1.利用IdleHandler特性,空闲执行;

六.优化总方针

异步,延迟,懒加载; 
技术.业务相结合
注意事项:
1.wall timecpu time的区别:cpu time才是优化方向;
2.按照systracecpu time跑满cpu  

微信图片_20210505103111.png

3.监控的完善:
 1).线上监控多阶段时间(Application,Activity生命周期间隔时间)
 2).处理聚合看趋势(上报后台查看)
4.收敛启动代码修改权限:结合Ci修改启动代码需要Review或通知
5.其他方案
 1.提前加载SharedPreferences;
  1).Multidex之前加载,利用此阶段的cpu,该阶段cpu未充分利用;
  2).复写getApplicationContext()返回this;
2.启动阶段不启动子进程
 1).子进程会共享cpu资源,导致主进程cpu紧张;
 2).注意启动顺序:避免App onCreate之前是启动四大组件,如Service,ContentProvider等;
3.类加载优化:提前异步类加载
 1).Class.forName()只加载类本身及其静态变量的引用类;
 2).new类实例,可以额外加载类成员变量的引用类;
4.启动阶段抑制GC
5.CPU锁频:将cpu频率拉起来

七.总结

1.获取方法耗时:
 1).常规方案实现的痛点
 2).AOP实现解决常规实现痛点;
 3).注意wall time与cpu time的无别
2.异步初始化:
 1).常规方案实现痛点
 2).启动器实现解决常规实现痛点
 3).注意痛点及启动器优势的理解
3.延迟初始化:
 1).常规方案实现痛点;
 2).结合IdleHandler(系统空闲时执行);
4.其他优化方案:
 1).提前异步SharedPreferences;
 2).启动阶段不启动子进程;
 3).提前异步类加载;

八.启动优化模拟面试

1.你做启动优化是怎么做的?
 1).分析现状,确认问题;
 2).针对性优化;
 3).长期保持优化效果;
 2.是怎么异步的,异步遇到问题没?
  1).体验演进过程;
  2).详细介绍启动器;
2.做了启动优化,有哪些容易忽略的注意点?
 wall time与cpu time的区别;
 注意延迟初始化的优化,首帧显示问题;
 介绍黑科技:异步类加载 cpu未跑满 拉高cpu核心数及频率;
3.版本迭代导致的启动变慢的解决方式?
 1).启动器;
 2).结合CI,代码review;
 3).完善监控(application activity生命周期监控);