启动优化
本文于 1290 天之前发表,文中内容可能已经过时。
app启动过程
- 点击图标启动
- LauncherApp通知AMS进行启动,LauncherActivity onPause
- AMS新建app进程,创建ActivityThread,创建ApplicationThread
- 通过ApplicationThread向AMS注册Binder
- 执行Application的onCreate方法
- 新建进入的Activity
- 执行Activity的onCreate方法,进行UI绘制等操作
启动分类
- 冷启动:从点击应用图标到UI界面完全显示且用户可操作的全部过程。
Click Event -> IPC -> Process.start -> ActivityThread -> bindApplication -> LifeCycle -> ViewRootImpl
- 热启动:直接从后台切换到前台。
优化方向
可优化Application、Activity的创建以及回调过程
- 提前展示一个window(欢迎页),给用户友好的提示
- 避免启动做繁重密集的初始化操作
- 过度绘制,网络,io等优化
优化检测
- adb命令检测
1
2// 其中的AppstartActivity全路径可以省略前面的packageName
adb shell am start -W [packageName]/[AppstartActivity全路径]
- ThisTime:最后一个Activity的启动耗时
- TotalTime:所有Activity的启动耗时
- WaitTime:表示AMS启动Activity的总耗时。
一般读取WaitTime,为Application和Activity的初始化过程耗时。(冷启动耗时)
优缺点:
- 线下使用方便,不能带到线上
- 非精确时间
- 自定义打点查看耗时
- 应用生命周期节点
- 启动的初始化方法节点
- 其他耗时业务,算法节点
优缺点:
- 精确,可上线
- 修改成本高
3.AOP打点
加入aspectjx库,打印出Application,Activity的耗时时间,进行针对优化
根路径build.gradle中添加
1 | classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0' |
app中build.gradle中添加
1 | apply plugin: 'android-aspectjx' |
使用如下:
1 |
|
4.TraceView
代码中开启
1 | Debug.startMethodTracing(); |
生成.trace文件,导入Android Studio,使用profile中的cpu查看文件生成的火炬图
优化方案
- 主题切换
设置自定义主题设置背景图,执行到onCreate方法后替换为Activiy的真实布局
2. 初始化分化
- MultiDex以及Tinker的初始化操作
- Application中的第三方组件的初始化
- 异步初始化组件,不阻塞主线程,设置异步线程为THREAD_PRIORITY_BACKGROUND
- 延迟初始化操作,再线程空闲时加载,
- EventBus、ota、bugly、migu、Linphone、Butterknife、地图、IOT
- 设置线程池初始化任务
- 仿照AsyncTask新建线程池,核心线程数为2-4个
- 任务使用该线程池加载,如有顺序,使用CountDownLatch进行处理
- 部分任务可以延迟加载,使用IdleHandler,在主线程空闲时加载
- Multidex预加载优化
优化方案
在使用Aspect进行时间的监测时,发现Application和Activity中的初始化三方进程耗费了大量时间,在初始化时,我们开启了百度OTA服务,Bugly监测服务,咪咕音乐服务,阿里IOT服务,日志监测服务,Linphone语音服务,Ifly语音服务等,这些串行起来是比较耗时的。
所以我们采用开启一个线程池的方案,在子线程启动这些服务,对于OTA,IOT,日志检测,Linphone等服务不需要在第一时间初始化,所以放到线程池中根据执行顺序分别初始化。但是对于咪咕、Ifly和bugly来说,需要第一时间初始化,才能进行后边的逻辑,所以我们将这些服务优先初始化,并联合CountDownLatch,当必须的服务初始化完成后,才进入下面的流程。
对于必须要在主线程进行初始化的操作,可能会造成主线程繁忙卡顿,所以使用IdleHandler方法,在主线程空闲时执行,
具体优化了40%,由2.3s压缩到1.4s。
如果由任务A,B,C,D,要求C在A之后执行,D在B之后执行,那么直接将A,C合并为一个任务,放入线程池中运行,B、D合并为一个任务,放入线池程中执行,如需决定AC和BD的顺序,那么可以按照AC、BD的顺序依次放入子线程中。
如何对IDLEHandler进行顺序划分?比如先执行B,在执行A
规划一个空闲队列,在Handler空闲时进行处理,每次出队优先级最高的,其他等到下次空闲在执行
2-4是怎么计算的?
核心线程数位2-4,计算方式是cpu核数-1,如果比2小,就选择2,比4大就选择4,中间就选它自己,
之所以 减掉这个1,是因为为了避免后台任务将 CPU 资源完全耗尽, 减掉的这个1 是留给我们 主线程 使用的。