Android是基于Linux的,每一个app都是一个独立的dalivk(4.4之后是art)虚拟机,每个虚拟机都是Linux中的一个独立进程,这样就保证了APP崩溃时,只是这一个虚拟机崩溃,而不会影响其它虚拟机
名词说明:
- 进程:进程就是系统中正在运行的程序,进程是操作系统的概念,每当我们执行一个程序时,对于操作系统来讲就是创建了一个进程,
- idle进程:系统创建的第一个进程,也是唯一一个不是fork出来的进程
- init进程:init进程(1号进程)由0号进程(idle进程)创建,完成系统的初始化,是第一个用户进程,是其他所有用户进程的父进程
- zygote进程:由init进程创建,负责后续Android应用程序框架层的其它进程的创建和启动工作,Android系统开启新进程的方式,是通过fork第一个zygote进程实现的
- fork:fork函数UNIX及类UNIX系统中的分叉函数,一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被称为子进程。fork函数被调用一次但返回两次。两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间。
- SystemServer进程:由zygote进程创建,SystemServer进程会启动所有系统的核心服务,如包管理服务PMS和应用程序组件管理服务AMS,硬件相关的Service等。然后它开启一个Socket接口来监听请求, 根据请求fork新的VM来管理新的App进程. 一旦收到新的请求, Zygote会基于自身预先加载的VM来fork一个新的VM创建一个新的进程
- AMS - ActivityManagerService :负责系统中 四大组件 的启动、切换、调度及应用程序的管理和调度等工作
- PMS - PackageManagerService:负责系统中Package的管理,应用程序的安装、卸载、信息查询等,博客说明:PackageManagerService类作用分析_h55l55的专栏-CSDN博客
- WMS - WindowManagerService:每个窗口都有一块Surface供自己涂鸦,所以需要对所有窗口的Surface进行协调管理,为所有窗口分配Surface,掌管Surface的显示顺序以及位置尺寸,控制窗口动画,并且还是输入系统的一个重要的中转站
笼统的开机过程:
手机开机-Linux启动,系统创建第一个进程-idle进程,由idle完成初始化操作,包括初始化页表,初始化中断向量表,初始化系统时间等。继而调用 fork(),创建第一个用户进程init进程,由init进程完成内核后续的初始化工作,并且会fork()一个zygote进程,负责后续Android应用程序框架层的其它进程的创建和启动工作,再由zygote进程fork()出一个SystemServer进程,这里主要会做的事情是
- 启动binder线程池,这是SystemServer与其他进程通信的基础
- 初始化Looper
- 创建SystemServiceManager对象,它会启动Android中的各种服务。包括AMS、PMS(PackageManagerService)、WMS(WindowManagerService)
- 启动桌面进程,这样才能让用户见到手机的界面。
- 开启loop循环,开启消息循环,SystemServer进程一直运行,保障其他应用程序的正常运行。
应用启动流程:
- 点击图标发生在Launcher应用的进程,startActivity()函数最终是由Instrumentation通过Android的Binder跨进程通信机制 发送消息给 SystemServer 进程; 在 SystemServer 中,启动进程的操作由ActivityManagerService 通过 socket 通信告知 Zygote 进程 fork 子进程(app进程)
- 开启主线程 app 进程启动后,首先是实例化 ActivityThread,并执行其main()函数:创建 ApplicationThread,Looper,Handler 对象,并开启主线程消息循环Looper.loop()。
- 创建并初始化 Application和Activity, ActivityThread的main()调用 ActivityThread#attach(false)方法进行 Binder 通信,通知SystemServer进程执行 ActivityManagerService#attachApplication(mAppThread)方法,用于初始化Application和Activity。 在SystemServer 进程中,ActivityManagerService#attachApplication(mAppThread)里依次初始化了Application和Activity,分别有2个关键函数: thread#bindApplication()方法通知主线程Handler 创建 Application 对象、绑定 Context 、执行 Application#onCreate() 生命周期 mStackSupervisor#attachApplicationLocked()方法中调用 ActivityThread#ApplicationThread#scheduleLaunchActivity()方法,进而通过主线程Handler消息通知创建 Activity 对象,然后再调用 mInstrumentation#callActivityOnCreate()执行 Activity#onCreate() 生命周期
- 布局&绘制
到这里Android启动与APP启动大致做了一个说明,接下来做启动优化
启动可分为:
- 冷启动
从点击应用图标到UI界面完全显示且用户可操作的全部过程。
- 热启动
直接从后台切换到前台。
- 温启动
只会重走Activity的生命周期,而不会重走进程的创建,Application的创建与生命周期等
虽然有三种,但我们的工作只有冷启动,因为热启动,我们不需要优化,温启动就看你们自己了,如果你非要在setContentView之前做大量的耗时操作,那我能说啥?
言归正传,看看我们能在什么地方做点什么?
冷启动
- 启动App
- 加载空白Window
- 创建进程
- 创建Application
- 启动主线程
- 创建MainActivity
- 加载布局
- 布置屏幕
- 首帧绘制
从这个过程可以看到,我们能做且可控并需要优化的地方,也就是Application和Activity的生命周期了
我们做的是启动优化,你应该不会在首个Activity中就把布局做的超级复杂且冗长从而导致耗时过多,用户看不到你想给他看的页面吧?
我们先对启动做一个计时,在生命周期中也加上计时,这样你就可以知道总耗时和某一个方法的耗时,总耗时-生命周期耗时=系统启动耗时,如果系统启动耗时很久,说明你手机很烂,如果系统耗时小于生命周期耗时,说明你可以想办法优化一下
获取总耗时的方法:
Logcat
在Android Studio Logcat中过滤关键字“Displayed”,可以看到对应的冷启动耗时日志
adb命令
adb shell am start -W 包名/AppStartActivity的全路径 如:com.seller.app.page.activity.SplashActivity
例子:
adb shell am start -W com.tencent.mobileqq/com.tencent.mobileqq.activity.SplashActivity
在log中有两个时间
TotalTime:应用的启动时间,其包括 创建进程 + Application初始化 + Activity初始化到界面显示 的过程
WaitTime:代表Ams启动activity的总耗时
这里我用了荣耀很烂的一个手机测试了qq
冷启动TotalTime不到600毫秒
热启动TotalTime不到200毫秒
而我建立了一个新的项目,什么都没有的情况下,TotalTime用时竟然不比qq的少甚至还要多一些😱,腾讯好屌,就这我还写个屁啊!
推荐两个博客个博客:
深入探索Android启动速度优化(上) - 掘金,他后面说的好多工具和方案,有兴趣的参考下吧
Android应用启动流程分析 - 掘金:启动流程分析