0. 前言
从Android手机打开开关,到我们可以使用其中的app时,这个启动过程到底是怎么样的?
1. 系统上电
当给Android系统上电,在电源接通的瞬间,CPU内的寄存器和各引脚均会被置为初始状态,CPU复位之后,程序指针会指向启动地址,从该地址读取并直接运行启动程序的可执行代码,或者将可执行代码与数据载入CPU内置的RAM中再运行。这一段代码,放在PC中,叫做BIOS,而在Android等嵌入式系统中就叫做Bootloader。
2. Bootloader引导程序
通过Bootloader引导程序,可以初始化硬件设备,建立内存空间映射图,为操作系统内核准备好环境,完成整个系统的加载启动任务。
(Bootloader是支持交互式操作的,一般刷机中会用到的Fastboot模式,就是在Bootloader启动之后,根据用户输入的指令(在手机上用音量键,Home健等操作),停留在当前状态。)
而正常情况下,Bootloader程序执行完成之后,就会开始加载Android操作系统。
3. 加载内核
我们知道Android操作系统的内核其实就是Linux内核。当引导程序启动之后,就会进入Linux的内核引导阶段。该阶段会初始化内核和设备驱动,接着启动内核,挂载文件系统,最后启动用户空间进程。
4. Init程序
Init程序是用户空间的第一个进程。
它负责解析一个叫做init.rc的配置文件,启动Android系统底层运行的一些服务进程。
init.rc文件定义了不同的Action和Service,其格式如下:
//Action是以关键字on来定义的,紧接着的boot是一个触发条件//如下面的action表明当Android启动(boot)的时候,要做些什么事情
// Action boot之后的class_start core等,是Init程序在开机时会启动的服务
//这些服务包括ueventd, servicemanager, void, zygote,installd, ril-daemon等,我们一般称它们为守护进程
on boot
...
class_start core
class_start main
//Service是以关键字servie来定义的,紧跟着的是其名字和其启动的文件
//如下会执行/system/bin/serviemanager文件,并将其命名为servicemanager
service servicemanager/system/bin/servicemanager #声明一个service,而servicemanager则是其名字
class core #optioin, 该service属于core类
usersystem $option,该service属于System用户
group System
Init程序同时也会开启属性服务,存放一些关于Android系统的全局系统设置信息。
最后,当所有的Action和Service都启动完成之后,Init程序就会进入一个无限的循环,监听系统中发生的事件,对一些事件进行处理,比如重启某些服务等。
5. ServiceManager
ServiceManager是Init程序启动的守护进程之一,它是Android中Binder通信机制的基础。
ServiceManager提供了注册,检索服务的功能。即这些服务由ServiceManager来统一进行管理。
而ServiceManager启动的过程做了三件事:
(1)通过open打开设备文件/dev/binder,把Binder设备映射到内核空间中,由于内核空间是所有进程共享的区域,所以借助这块区域,可以实现不同进程间资源的共享,从而达到进程间通信。
(2)通过IO控制命令BINDER_SET_CONTEXT_MGR将当前进程注册到Binder驱动中,Binder驱动便会为他在内核空间创建一个Binder实体,并且将其句柄设置为0,从句柄值也能看出来,这个节点在Binder驱动中是唯一的,系统会将跨进程服务注册到它的服务列表里面,服务列表中存储的便是服务名字+这个服务在Binder驱动中的句柄。
(3)最终ServiceManager也是会进入一个无限循环,等待客户端的IPC请求。
6. Zygote
Zygote也是由Init程序启动的其中一个服务,Zygote进程是Init进程fork()出的第一个横跨Java/C /C++的进程。
6. 1 第一个虚拟机
Zygote启动过程中会调用AndroidRuntime.cpp中的startVm方法来创建第一个虚拟机。之后所有的Dalvik虚拟机都是由Zygote孵化出来的,这是因为Android中每一个应用都有着自己的进程,而每一个进程中都着一个Dalvik虚拟机,如果每起一个应用就创建一个虚拟机,效率太低,而通过复制Zygote,不仅可以提高创建的速度,还能够共享系统和框架的资源,可以大幅度提高应用程序启动的效率。
6. 2 SystemService
(1)首先会创建一个socket。
(2)Zygote进程会孵化出一个子进程SystemService,我们大部分的系统服务比如ActivityManagerService、WindowManagerService等都是该进程内的一个线程,这些服务会通过调用ServiceManager.addService方法添加到ServiceManager中的服务列表中,这样我们的系统服务就已经都注册到了ServiceManager里面了。
ActivityManagerService启动后就会启动桌面应用,然后跟Zygote启动之初的Socket进行通信,告诉Zygote一切已经就绪。
(3)Zygote知道已经启动成功,就开始进入一个无限的循环,在Socket上等待用户开启应用的请求到来。
【JNI拓展】
如果是通过System.loadLibrary(XX)把动态库加载到内存中,会调用dlopen()函数打开一个so文件,并调用 dlsym() 函数,查看相应so文件的JNI_OnLoad()函数指针,并执行相应函数。在该函数中通过一个gMethods方法,得到了记录的Java层和C/C++层方法的一一映射关系。
至此,从Android手机上电的那一刻起,到加载内核,到最后显示Home桌面的整个过程就结束了。
7. APK的安装过程
Android系统启动的过程中会启动一个应用程序管理服务PackageManagerService(系统启动的时候由SystemServer组件启动),这个服务负责扫描/system/framework、/system/app、/vendor/app、/data/app、/data/app-private五个目录下的APK文件,然后解析该APK中的AndroidManifest.xml以获得应用程序相关信息,其中最重要的就是依据sharedUserId这个配置来让系统确定每个APK运行在哪个进程。
继而为这个APK分配Linux用户ID、用户组ID以便APK在系统中可以获取到合适的运行权限、资源访问权限。
最后将之前获得的权限和APK安装信息保存到本地的一个配置文件中,以便下次在安装这些APK时可以将需要的APK信息很快速的恢复回来。
8. 应用启动流程
8.1 应用的冷热启动
Android中的冷启动指当启动应用时,后台没有该应用的进程。这时系统会重新创建一个新的进程分配给该应用,所以会先创建和初始化Application类,再创建和初始化MainActivity类(包括一系列的测量、布局、绘制),最后显示在界面上。
Android中的热启动指当启动应用时,后台已有该应用的进程(如按back键、home键,应用虽然会退出,但是该应用的进程是依然会保留在后台,可进入任务列表查看),热启动就不会走Application这步了。
8.2 应用的启动流程
(1)点击桌面App图标,Launcher进程采用Binder IPC向system_server进程发起startActivity请求;
system_server进程接收到请求后,向Zygote进程发送创建进程的请求;随后Zygote进程fork出一个新的进程分配给该应用。
(2)应用进程通过Binder IPC向sytem_server进程发起attachApplication请求;system_server进程在收到请求后,进行一系列准备工作,再通过Binder IPC向应用进程发送scheduleLaunchActivity请求。
(3)应用进程的Binder线程(ApplicationThread)在收到请求后,通过handler向主线程发送LAUNCH_ACTIVITY消息;主线程在收到Message后,依次创建和初始化Application类、创建MainActivity类、加载Activity的主题、配置Activity层级上的一些属性、再inflate布局、当onCreate/onStart/onResume方法都走完了后,最后进行contentView的measure/layout/draw显示在界面上,至此应用启动完成。后半段的启动流程如下:
Application的构造器方法——>attachBaseContext()——>onCreate()——>Activity的构造方法——>onCreate()——>配置主题中背景等属性——>onStart()——>onResume()——>测量布局绘制显示在界面上
可以通过adb shell命令的方式进行测量应用的启动时间:
adb shell am start -W [packageName]/[packageName.MainActivity]
8.3 启动优化建议
(1)在Application的构造器方法、attachBaseContext()、onCreate()方法中不要进行耗时操作的初始化。
(2)对于MainActivity,在用户看到第一帧之前,需要进行测量、绘制布局,所以应该尽量减少布局的层次,考虑StubView的延迟加载策略,当然在onCreate、onStart、onResume方法中也要避免做耗时操作。
(3)插入一个预加载画面,完成一些耗时操作,这样用户体验比较好。
感谢:
http://blog.csdn.net/linmiansheng/article/details/37728903
http://blog.csdn.net/u010687392/article/details/50518343
http://www.2cto.com/kf/201607/528367.html
http://www.jianshu.com/p/0b0d6f684580
http://www.tuicool.com/articles/YVbURfQ