Android应用启动速度优化
应用启动流程总结:
①点击启动一个App,Launcher进程采用Binder IPC向ActivityManagerService发起startActivity请求;
②ActivityManagerService接收到请求后,向zygote进程发送创建进程的请求;
③Zygote进程fork出新的子进程,即App进程;
④App进程通过Binder IPC向sytem_server进程发起绑定Application请求;
⑤system_server进程在收到请求后,进行一系列准备工作后,再通过binder IPC向App进程发送scheduleLaunchActivity请求;
⑥App进程的binder线程(ApplicationThread)在收到请求后,通过handler向主线程发送LAUNCH_ACTIVITY消息;
⑦主线程在收到Message后,通过发射机制创建目标Activity,并回调Activity.onCreate()等方法。
从这里的应用启动过程来看,我们要对应用启动速度进行优化,能做的就是在application和launcherActivity的启动进行优化,即不能进行耗时操作。
冷启动、温启动、热启动概念
冷启动
冷启动指的是该应用程序在此之前没有被创建,发生在应用程序首次启动或者自上次被终止后的再次启动。简单的说就是app进程还没有,需要创建app的进程启动app。
比如开机后,点击屏幕的app图标启动应用。
冷启动的过程主要分为两步:
1)系统任务。加载并启动应用程序;显示应用程序的空白启动窗口;创建APP进程
2)APP进程任务。启动主线程;创建Activity;加载布局;屏幕布局;绘制屏幕
其实这不就是APP的启动流程嘛?所以冷启动是会完整走完一个启动流程的,从系统到进程。
温启动
温启动指的是App进程存在,但Activity可能因为内存不足被回收,这时候启动App不需要重新创建进程,只需要执行APP进程中的一些任务,比如创建Activity。
比如返回主页后,又继续使用其他的APP,时间久了或者打开的应用多了,之前应用的Activity有可能被回收了,但是进程还在。
所以温启动相当于执行了冷启动的第二过程,也就是APP进程任务,需要重新启动线程,Activity等。
热启动
热启动就是App进程存在,并且Activity对象仍然存在内存中没有被回收。
比如app被切到后台,再次启动app的过程。
所以热启动的开销最少,这个过程只会把Activity从后台展示到前台,无需初始化,布局绘制等工作。
那我们所要做的启动速度优化呢,就是对于冷启动这种情况进行的。
启动过程需要经过哪些生命周期方法呢?
-> Application 构造函数
-> Application.attachBaseContext()
-> Application.onCreate()
-> Activity 构造函数
-> Activity.setTheme()
-> Activity.onCreate()
-> Activity.onStart
-> Activity.onResume
-> Activity.onAttachedToWindow
-> Activity.onWindowFocusChanged
启动开始标识为Application.attachBaseContext(),启动结束的最后标识是LauncherActivity的onWindowFocusChanged方法调用完成。
1、Application的onCreate流程,对于大型的APP来说,通常会在这里做大量的通用组件的初始化操作;
建议:很多第三方SDK都放在Application初始化,我们可以放到用到的地方才进行初始化操作。
2、Activity的onCreate流程,特别是UI的布局与渲染操作,如果布局过于复杂很可能导致严重的启动性能问题;
App进程的创建等环节我们是无法主动控制的,可以优化的也就是Application、Activity创建以及回调等过程。
解决方案1 :添加一个默认的启动页的Theme能从现象解决这个问题,但是并没有从根本上解决启动慢的问题。
解决方案2:
Application中主要做了各种三方组件的初始化,参数设置,启动各种服务等;
对于过多的初始化任务,我们考虑以下优化方案:
1.考虑异步初始化三方组件,不阻塞主线程;
2.延迟部分三方组件的初始化;
启动耗时统计:
如果是本地调试的话,统计启动时间还是很简单的,通过命令行方式即可:
adb shell am start -W packagename/activity
例如:
adb shell am start -W com.xxx.xxxx/com.xxxx.login.activity.LauncherActivity
得到冷启动时间:
WaitTime
返回从 startActivity 到应用第一帧完全显示这段时间. 就是总的耗时,包括前一个应用 Activity pause 的时间和新应用启动的时间;
ThisTime
表示一连串启动 Activity 的最后一个 Activity 的启动耗时;
TotalTime
表示新应用启动的耗时,包括新进程的启动和 Activity 的启动;
一般来说,只需查看得到的TotalTime,即应用的启动时间,其包括 创建进程 + Application初始化 + Activity初始化到界面显示 的过程。
CPU profiler使用
使用CPU profiler来解决哪些性能问题
我们可以使用CPU profiler来分析CPU耗时问题,解决由于CPU资源消耗而产生的性能问题。
1.我们可以使用CPU profiler来查看主线程中每个方法的耗时情况,以及每个方法的调用栈,可以很方便的分析卡顿产生的原因,以及定位到具体的代码方法。
2.我们可以使用它来进行启动优化分析,查看APP启动时的耗时问题。
3.我们可以使用它来查看线程的执行情况,定位线程的CPU资源占用等问题。
开启使用CPU profiler追踪启动过程:
1.事件时间轴:显示应用中的 Activity 在其生命周期内不断转换而经历各种不同状态的过程
2.CPU 时间轴:显示应用的实时 CPU 使用率(以占总可用 CPU 时间的百分比表示)以及应用当前使用的线程总数。
3.线程活动时间轴:列出属于应用进程的每个线程,并使用下面列出的颜色在时间轴上指示它们的活动。
1、Trace Java Methods
会记录每个方法的时间、CPU信息。对运行时性能影响较大。
2、Sample Java Methods
相比于Trace Java Methods会记录每个方法的时间、CPU信息,它会在应用的Java代码执行期间频繁捕获应用的调用堆栈,对运行时性能的影响比较小,能够记录更大的数据区域。
跟踪 Java 方法:在运行时检测应用,以在每个方法调用开始和结束时记录一个时间戳。系统会收集并比较这些时间戳,以生成方法跟踪数据,包括时间信息和 CPU 使用率。
开启对程序追踪模式,启动程序,得到追踪的结果:
使用“Call Chart”标签检查跟踪数据
Call Chart 标签提供方法跟踪数据或函数跟踪数据的图形表示形式,其中调用的时间段和时间在水平轴上表示,而其被调用方显示在垂直轴上。对系统 API 的调用显示为橙色,对应用自有方法的调用显示为绿色,对第三方 API(包括 Java 语言 API)的调用显示为蓝色。
这里得到追踪的启动过程各个方法调用的时长,针对耗时较长的方法进行分析和优化,解决耗时问题。
CPU Profile各查看方法比较:
找到这些布局加载耗时,或者第三放库初始化,或者我们自己的方法比较耗时,主要的处理思路就是合理的使用异步初始化、延迟初始化、懒加载机制。
使用启动器来异步加载初始化任务:
启动器流程:
(1)代码Task化,启动逻辑抽象为Task
(2)根据所有任务依赖关系排序生成一个有向无环图
(3)多线程按照排序后的优先级依次执行
比如创建一个ARouterTask:
class ARouterTask(val application: BaseApplication) : Task("") {
override fun run() {
if (BuildConfig.DEBUG) {
ARouter.openLog()
ARouter.openDebug()
}
ARouter.init(application)
}
}
将task添加进project的task列表中:
fun createCommonTaskGroup() : Task {
var arouterTask = ARouterTask(this)
var refreshTask = RefreshTask()
var builder = Project.Builder()
builder.add(arouterTask)
builder.add(refreshTask)
return builder.create()
}
或者用after给task带有顺序:
Project.Builder builder = new Project.Builder();
builder.add(a);
builder.add(b).after(a);
builder.add(c).after(a);
builder.add(d).after(b, c);
builder.add(e).after(a);
Project group = builder.create();
最后,manager添加project并start各个异步任务。
AlphaManager.getInstance(applicationContext).addProject(createCommonTaskGroup())
AlphaManager.getInstance(applicationContext).start()
参考:
https://blog.csdn.net/u011578734/article/details/109496667
https://juejin.cn/post/6844904093786308622#heading-132
Android启动优化--异步优化
阿里异步启动框架