冷启动优化主要优化两个方面
- Application 性能优化
- App启动页性能优化
业务优化不在本章优化范围内。本章主要量化性能检测数据和问题,并针对其做性能优化和修改。
冷启动速度优化
冷启动耗时统计
adb shell am start -S -W [packageName] / [activityName]
- -s 表示杀死程序重启
- -w 启动并输出相应的数据
- packageName applicationId
- activityName 启动页的名称,Luauncher Activity
执行上述命令,成功唤起应用,并在控制台输出如下数据:
LaunchState : 启动模式,这里是冷启动
waitTime:系统启动耗时 = TotalTime +系统资源时间
totalTime:应用自身启动时间 = 该Activity启动 + Application系统资源启动时间
对于冷启动,我们一般关注的是totalTime,该时间大致概括为:Application的onCreate到Activity的onWindowFouchChange方法的总和,这个初步理解为从应用启动到Activity获取焦点,业务执行的总时间。
冷启动耗时堆栈观察方法
在API>=26的版本中,建议使用CPU profile 或者Debug.startMethodTracing进行监控导出trace文件。
-
CPU profile
这种方式不知道冷启动怎么监控,但是可以手动采样启动后某一段时间。
- Debug.startMethodTracing
由于冷启动涉及到业务应用层面是:Activity启动+Application系统资源启动时间。所以,我们在Application中开始采集,在第一个Activity的onWindowFouchChange里面停止采集,并输出trace文件。
//Application 的onCreate代码
fun applicationTrace(){
// 保存trace的文件
val file = File(Environment.getExternalStorageState(),"myApp.trace")
//采集方式有两种,根据需要选择其一
//通过采样的方式追踪堆栈信息
// if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ){
// //通过采样方式追踪堆栈信息,需要制定文件保存目录,文件大小 单位M,采样间隔时间us
// Debug.startMethodTracing(file.absolutePath,8,1000)
// }
//第二种方式全采样
Debug.startMethodTracing(file.absolutePath)
}
//MainActivity的代码
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
Debug.stopMethodTracing()
}
Trace日志文件的阅读
在导出.trace文件后,直接拖到android studio中,便可以对程序运行的堆栈进行分析。
上图是trace文件打开后的效果,是基于CPU和线程的运行状况,针对启动优化,需要关注几个点:
(1)CPU运行时间轴,横向拖动可以选择时间范围。
(4)当前设备CPU轮转的线程:点击可以选择需要查看的线程,我们重点关注主线程
(2)当前选择线程,跟随时间轴各个方法栈的调用情况和耗时情况。其不同颜色分别代表:
- 黄色 :android系统方法(Framwork层代码,如果需要最终最底层的方法,需要C/C++代码调用栈。
- 蓝色:代表java JDK方法
- 绿色:属于当前进程执行的方法,包括类加载器和我们的业务代码。(启动优化主要是这部分)
(3)各个方法栈的调用顺序和耗时情况。可以选择不用的排序方式和视图。
Application优化
通过trace文件,我们发现,耗时最长的是Application的onCreate方法,其中initMudleFactory、initURS、Unicore.init、initUmeng都比较耗时。
查看这几个三方库初始化源码,发现都做了现场安全,所以直接放到子线程中执行。
对于一些不能放到子线程中执行的三方库的初始化,我们可以使用IdleHandle去处理,它会在主线程空闲的时候去执行。
Launcher Activity优化
思路还一样,查看trace文件,在Activity的onCreate中有三个地方耗时比较多,initActionbar,EventBus.register,setContentView。下面分别做优化:
1.initActionbar
通过trace图可以看到最耗时的是getSupportActionBar,实际的欢迎页面不需要actionBar,所以,我们重写了父类方法,把super方法去掉了。
2.EventBus.register
Eventbus2.0是通过注解加反射实现的,性能相对差,所以我们可以升级到3.0.这个是通过编译生成文件,不会对运行时性能造成影响。
3.setContentView
AsyncLayoutInflater(this).inflate(
R.layout.activity_welcome, null
) { view: View?, resid: Int, parent: ViewGroup? ->
setContentView(
view
)
}
通过异步加载布局是一个方面,优化布局是另一个方面。
优化方法总结
1.合理的使用异步加载、延迟初始化、懒加载机制去初始化三方库。
2.主线程中避免做耗时操作,比如io,数据的读写。
3.简化launcher Activity的布局,采用两种方式优化:
- 采用ConstraintLayout减少布局的层数,降低过度渲染
- 使用 androidx.asynclayoutinflater:asynclayoutinflater进行异步加载xml
4.合理的使用idleHandler进行延迟初始化
/**
* 需要在当前线程中处理耗时任务,并且并不需要马上执行的话,可以使用IdleHandler
* 这样该任务可以消息队列空闲时,被处理
*/
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
//此处添加处理任务
//返回值为false:则执行完毕之后移除这条消息,
//返回值为true:则则执行完毕之后依然保留,等到下次空闲时会再次执行,
return false;
}
});
5.使用严苛模式(Strict Mode)
该模式并不能自动帮我们优化性能,但是可以检测出我们的代码或者三方库代码会阻塞Main线程的事情(例如磁盘操作,网络操作),并将他们提示出来,我们在开发阶段可以修改掉。
/**
* 开启严苛模式,当代码有违规操作时,可以通过Logcat或崩溃的方式提醒我们
*/
private void startStrictMode() {
if (BuildConfig.DEBUG) { //一定要在Debug模式下使用,避免在生产环境中发生不必要的崩溃和日志输出
//线程检测策略
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads() //检测主线程磁盘读取操作
.detectDiskWrites() //检测主线程磁盘写入操作
.detectNetwork() //检测主线程网络请求操作
.penaltyLog() //违规操作以log形式输出
.build());
//虚拟机检测策略
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects() //检测SqlLite泄漏
.detectLeakedClosableObjects() //检测未关闭的closable对象泄漏
.penaltyDeath() //发生违规操作时,直接崩溃
.build());
}
}