一.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
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;
2.异步优化
核心思想:子线程分担主线程任务,并行减少时间
注意事项:
1).不符合异步要求;
2).需要在某阶段完成;
3).区分CPU密集型和IO密集型任务;
3.异步优化最优解:
1).常规异步痛点:
a.代码不优雅;
b.场景不好处理(依赖关系);
c.维护成本高;
2).启动器(重点)
启动器流程:
1.代码Task化,启动逻辑抽象为Task;
2.根据所有任务依赖关系排序生成一个有向无环图;
3.多线程按照排序后的优先级依次执行;
4.解决必须在主线程执行及有依赖关系的task;
4.延迟初始化方案
常规初始化痛点:
a.时机不便控制;
b.导致Feed卡顿;
更优方案:
核心思想:对延迟任务进行分批初始化
1.利用IdleHandler特性,空闲执行;
六.优化总方针
异步,延迟,懒加载;
技术.业务相结合
注意事项:
1.wall time与cpu time的区别:cpu time才是优化方向;
2.按照systrace及cpu time跑满cpu
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生命周期监控);