-
背景介绍
- 启动是第一体验
- 八秒定律
-
启动分类
-
-
冷启动
耗时最多,衡量标准
需要了解冷启动流程
-
热启动
最快
-
温启动
较快
-
-
-
相关任务
- 冷启动之前
- 启动App
- 加载空白Window
- 创建进程
- 随后任务
- 创建Application
- 启动主线程
- 创建MainActivity
- 加载布局
- 布置屏幕
- 首帧绘制
- 冷启动之前
-
优化方向
Application和Activity生命周期
-
adb命令
adb shell am start -W packagename/packagename.首屏Activity eg:adb shell am start -W com.iyueke.kids/com.iyueke.kids.ui.detail.splash.SplashActivity
ThisTime:最后一个Activity启动耗时
TotalTime:所有Activity启动耗时
WaitTime:AMS启动Activity的总耗时
线下使用方便,不能带到线上,可以参照竞品
非严谨、精确时间
-
手动打点
启动时埋点,启动结束埋点,二者差值
LaunchTimer类辅助打点
开始时间:Application#attachBaseContext
结束时间误区:Activity#onWindowFocusChanged只是首帧时间
结束时间正解:真实数据展示,数据第一条展示
精确,可带到线上,推荐使用
避开误区,采用数据第一条展示
-
traceview
图形的形式展示执行时间、调用栈等
信息全面,包含所有线程
使用方式:
Debug.startMethodTracing("App")
Debug.stopMethodTracing("")
- 生成文件在sd卡:Android/data/packagename/files
- 在Android Studio中打开生成文件
运行时开销严重,整体都会变慢
可能会带偏优化方向
traceview和cpu profiler
-
systrace
结合Android内核的数据,生成Html报告
API 18以上使用,推荐TraceCompat
使用方式:
TraceCompat.beginSection("ApponCreate")
TraceCompat.endSection()
- 运行python脚本:
python systrace.py -t 10 [other-options][categories]
轻量级,开销小
直观反映cpu利用率
cputime和walltime区别
- cputime是代码消耗cpu的时间(重点指标)
- walltime是代码执行时间
- cputime为什么和walltime不一样,举例:锁冲突
-
常规方式
背景:需要知道启动阶段所有方法耗时
实现:手动埋点
long time = System.currentTimeMillis()
long cost= System.currentTimeMillis() - time
- 或者
SystemClock.currentThreadTimeMillis()
侵入性强、工作量大
-
AOP介绍
Aspect Oriented Programming,面向切面编程
- 针对同一类问题的统一处理
- 无侵入添加代码
AspectJ使用
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0'
implementation 'org.aspectj:aspectjrt:1.8.+'
apply plugin: 'android-aspectjx'
Join Points:程序运行时的执行点,可以作为切面的地方
- 函数调用、执行
- 获取、设置变量
- 类初始化
PointCut:带条件的Join Points
Advice:一种Hook,要插入代码的位置
- Before:PointCut之前执行
- After:PointCut之后执行
- Around:PointCut之前、之后分别执行
语法简介
- Before:Advice,具体插入位置
- execution:处理Join Point的类型,call、execution
- 匹配规则
- onActivityCalled:要插入的代码
具体实现见:PerformanceAop
优点:无侵入性、修改方便
-
优化技巧
Theme切换:感觉上的快
步骤
-
lanucher.xml
-
<activity android:theme="@style/Theme.Splash">
-
@Override protected void onCreate(Bundle savedInstanceState) { // super.onCreate之前切换回来 setTheme(R.style.AppTheme); super.onCreate(savedInstanceState); }
-
-
异步优化
核心思想:子线程分担主线程任务,并行减少时间
// 线程池 ExecutorService service = Executors.newFixedThreadPool(CORE_POOL_SIZE); service.submit(new Runnable() { @Override public void run() { initBugly(); } }); // ...
// 必须被满足一次 CountDownLatch mCountDownLatch = new CountDownLatch(1); service.submit(new Runnable() { @Override public void run() { // 需要在某阶段完成 initBugly(); mCountDownLatch.countDown(); } }); try { // 等待 mCountDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); }
异步优化注意事项:
- 不符合异步要求
- 需要在某阶段完成
- 区分CPU密集型和IO密集型任务
-
常规异步痛点
- 代码不优雅
- 场景不好处理(依赖关系)
- 维护成本高
-
启动器介绍
核心思想:充分利用CPU多核,自动梳理任务顺序
启动器流程
- 代码Task化,启动逻辑抽象为Task
- 根据所有任务依赖关系排序生成一个有向无环图
- 多线程按照排序后的优先级依次执行
-
启动器实战
TaskDispatcher.init(PerformanceApp.this); TaskDispatcher dispatcher = TaskDispatcher.createInstance(); dispatcher.addTask(new InitAMapTask()) .addTask(new InitStethoTask()) .addTask(new InitWeexTask()) .addTask(new InitBuglyTask()) .addTask(new InitFrescoTask()) .addTask(new InitJPushTask()) .addTask(new InitUmengTask()) .addTask(new GetDeviceIdTask()) .start(); dispatcher.await();
-
常规方案
-
New Handler().postDelayed
-
数据展示后调用
-
new DispatchRunnable(new DelayInitTaskA()).run(); new DispatchRunnable(new DelayInitTaskB()).run();
常规初始化痛点:
- 时机不便控制
- 导致页面卡顿
-
-
更优方案
核心思想:对延迟任务分批初始化
利用IdleHandler特性,空闲执行
DelayInitDispatcher delayInitDispatcher = new DelayInitDispatcher(); delayInitDispatcher.addTask(new DelayInitTaskA()) .addTask(new DelayInitTaskB()) .start();
优点:
- 执行时机明确
- 缓解页面卡顿
- 优化总方针
- 异步、延迟、懒加载
- 技术、业务相结合
- 注意事项
- wall time与cpu time
- cpu time才是优化方向
- 按照systrace及cpu time跑满cpu
- 监控的完善
- 线上监控多阶段时间(App、Activity、生命周期间隔时间)
- 处理聚合看趋势
- 收敛启动代码修改权限
- 结合ci修改启动代码需要Review或通知
- wall time与cpu time
- 其它方案
- 提前加载SharedPreferences
- Multidex之前加载,利用此阶段cpu
- 复写getApplicationContext()返回this
- 启动阶段不启动子进程
- 子进程会共享CPU资源,导致主进程CPU紧张
- 注意启动顺序:App onCreate 之前是ContentProvider
- 类加载优化:提前异步类加载
- Class.forName() 只加载类本身及其静态变量的引用类
- new 类实例可以额外加载类成员变量的引用类
- 启动阶段抑制GC
- CPU锁频
- 提前加载SharedPreferences
- 你做启动优化是怎么做的?
- 分析现状、确认问题
- 针对性优化
- 长期保持优化效果
- 是怎么异步的,异步遇到问题没有
- 体现演进过程
- 详细介绍启动器
- 你做了启动优化,觉得有哪些容易忽略的注意点
- cpu time与wall time
- 注意延迟初始化的优化
- 介绍下黑科技
- 版本迭代导致的启动变慢有好的解决方式吗
- 启动器
- 结合CI
- 监控完善