Android 系统启动过程
1. init进程启动过程
- 开机键引导芯片从ROM加载BootLoader到RAM。
- 引导BootLoader拉起Android OS。
- Linux内核启动,执行init.cpp的main函数,创建init进程。
- init进程中创建和挂载启动所需的文件目录,初始化属性服务、启动属性服务、解析init.rc配置文件并启动Zygote进程。
2. Zygote进程启动过程
- Zygote进程
Android系统中DVM(Dalvik虚拟机)和ART、应用程序进程以及系统关键服务的SystemServer进程都是由Zygote创建的。
Zygote(孵化器)是通过fork(复制)进程的形式来创建应用程序进程以及SystemServer,同时Zygote进程在启动的时候会fork Dalvik/ART
所以由Zygote创建的进程都拥有一个独立的DVM/ART虚拟机的实例副本。这也是为什么Android中应用程序是独立、安全、稳定的,不会因为一个进程出现错误导致所有进程被Kill。
- Zygote进程启动
- 创建AppRuntime调用start方法,启动Zygote进程。
- 创建Java虚拟机并为虚拟机注册JNI方法。
- 通过JNI方法调用ZygoteInit.java的main函数进入Zygote的Java框架层。
- 预加载类和资源,并启动SystemServer。
- 通过registerZygoteSocket方法创建服务器端的Socket,并通过runSelectLoop方法等待AMS的请求来创建新的应用程序进程。
- 时序图:
3. SystemServer进程启动过程
- SystemServer进程
SystemServer进程主要用于创建系统服务,例如AMS、WMS、和PMS都是由它来创建的。
- Service进程启动
- 启动Binde线程池,这样就可以与其他进程进行通信。
- 创建SystemServiceManager,用于对系统的服务进行创建、启动生命周期管理。
- 启动各种系统服务(引导服务、核心服务、其他服务)。
- 时序图
4. Launcher启动过程
- Launcher
当系统启动到最后一步时,会启动一个应用程序,也就是我们通常看到的应用桌面,它被称作Launcher,在Launcher程序启动的时候会请求PackageManagerService来获取当前系统已安装的应用程序,并将其应用信息封装成快捷方式展现在我们的屏幕桌面上,这样用户通过点击应用图标就可以启动应用程序了。
所以Launcher由两个特点:
- 作为Android系统的启动器,用于启动应用程序。
- 作为Android系统的桌面,用于显示和管理应用程序的快捷图标或其他桌面组件。
- 时序图
总结:Android系统启动流程
- 启动电源以及系统启动。(引导芯片从固化在ROM的预定义地方执行,加载主引导BootLoader到RAM)
- 引导程序BootLoader。(把系统OS拉起来)
- Linux内核启动。(设置缓存、被保护存储器、计划列表、加载驱动。内核完成时,首先在系统文件中寻找init.rc文件,并启动init进程)
- init进程启动。(初始化和启动属性服务,并启动Zygote进程)
- Zygote进程启动。(创建Java虚拟机并为Java虚拟机注册JNI方法,创建服务端Socket,启动SystemServer进程。)
- SystemServer进程启动。(启动Binder线程池和SystemServiceManager,并且启动各种系统服务。)
- Launcher启动。(被SystemServer进程启动的AMS会启动Launcher,Launcher启动后会将已安装的应用程序快捷方式图标展示到桌面。)
图示:
应用程序启动过程
如果启动一个应用程序,AMS会检测该应用程序进程是否存在,如果不存在则向Zygote进行请求创建。
我们知道Zygote的Java框架层中创建了一个服务端的Socekt,用于等待AMS创建新的应用进程请求。AMS发起请求后,Zygote会fock自身进程来创建应用程序,这样应用程序进程在启动时就拥有了虚拟机实例,同时也创建了Binder线程池和消息循环,这样运行在应用程序进程中的应用程序就可以进行进程间通信以及消息处理了。
1. AMS发送启动应用进程请求时序图
2. Zygote接收请求并创建应用程序进程时序图
我们看到该时序图和上文提到的SystemServer启动过程时序图有相似之处。共同点都是从ZygoteInit调用RuntimeInit的applicationInit开始,后续执行流程一样。
注:为什么Android会选择抛出MethodArgsCall异常的方式来调用方法而不是直接调用SystemService的main方法或ActivityThread的main方法?
是因为抛出异常的处理会清除所有的设置过程需要的堆栈帧(内存优化)
3.消息循环创建过程
ActivityThread是用于管理当前应用程序进程的主线程。其部分关键源码如下:
public static void main(String[] args){
...
// 创建主线程Looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if(sMainThreadHandler == null){
// 创建主线程H类
sMainThreadHandler = thread.getHandler();
}
if(false){
Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
// Looper开始工作
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
四大组件工作过程
1. 根Activity启动过程
Activity启动分为:根Activity启动和普通Activity启动。而普通Activity与根Activity启动方式类似,根Activity启动更具有代表性。
根Activity启动过程比较复杂,大致分为3个部分:Launcher请求AMS过程、AMS到ApplicationThread调用过程、ActivityThread启动Activity。
- Launcher请求AMS时序图:
注:IActivityManager为AMS的代理对象,它是在Instrumentation中通过ActivityManager.getService()方法获取的。见下文ActivityManager源码:
public static IActivityManager getService(){
return IActivityManagerSingleton.get();
}
private static final Singleton IActivityManagerSingleton = new Singleton(){
@Override
protected IActivityManager create(){
// Binder类型的AMS引用
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
// 所以此处的IActivityManager实则是AMS的代理对象
final IActivityManager am = IActivityManager.Stub.asInteface(b);
return am;
}
}
// Android8.0使用的AIDL的形式来与AMS进程间通信的,Android7.0则是使用类似于AIDL的ActivityManagerProxy代理对象来实现的。
- AMS到ApplicationThread的调用时序图:
注:ApplicationThread是ActivityThread的内部类,它继承自IApplicationThread.Stub。它用于和AMS进行进程间通信
AMS与应用程序进程通信图示:
- ActivityThread启动Activity的过程:
注:H类为ActivityThread的内部类,它继承Handler。由于ApplicationThread的代码是运行在Binder线程池中的,所以通过H类将代码切回到主线程中执行后续操作
2.根Activity启动过程中涉及的进程
根Activity启动过程会涉及4个进程,分别是Zygote进程、Launcher进程、AMS所在进程(SystemServer进程)、应用程序进程。
- 关系图:
- 时序图:
注:如果是普通Activity启动过程会涉及几个进程?答案是两个,AMS所在进程和应用程序进程。
3. Service的启动过程
Service的启动过程和Activity类似,它分为两个过程,分别是ContextImpl到AMS、ActivityThread启动Service。
- ContextImpl到AMS时序图
- ActivityThread启动Service时序图
Android开发者如何像高级进阶?
不知不觉自己已经做了几年开发了,由记得刚出来工作的时候感觉自己能牛逼,现在回想起来感觉好无知。懂的越多的时候你才会发现懂的越少。
如果你的知识是一个圆,当你的圆越大时,圆外面的世界也就越大。
在我学习的过程中,最开始是在网上找了很多资料,毕竟这些资料是我们开始最快速的学习方法,这里我放上我这些年在网上收集到的资料,然后再以我的工作经验给大家总结一下,让你们少走些弯路,提取一些目前互联网公司最主流的Android开发架构技术,希望能帮助到大家!
作为移动开发的我们该如何突破瓶颈呢?
1、确定好方向,梳理成长路线图
不用多说,相信大家都有一个共识:无论什么行业,最牛逼的人肯定是站在金字塔端的人。所以,想做一个牛逼的程序员,那么就要让自己站的更高,成为技术大牛并不是一朝一夕的事情,需要时间的沉淀和技术的积累。
关于这一点,在我当时确立好Android方向时,就已经开始梳理自己的成长路线了,包括技术要怎么系统地去学习,都列得非常详细。
知识梳理完之后,就需要进行查漏补缺,所以针对这些知识点,我手头上也准备了不少的电子书和笔记,这些笔记将各个知识点进行了完美的总结。
2、通过源码来系统性地学习
只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。
真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。
3、阅读前辈的一些技术笔记
4、刷题备战,直通大厂
历时半年,我们整理了这份市面上最全面的安卓面试题解析大全
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。
如何使用它?
1.可以通过目录索引直接翻看需要的知识点,查漏补缺。
2.五角星数表示面试问到的频率,代表重要推荐指数
以上文章中的资料,均可以免费分享给大家来学习,无论你是零基础还是工作多年,现在开始就不会晚。
以上内容均放在了开源项目:【github】 中已收录,大家可以自行获取(或者关注主页扫描加微信获取)。
最后:
学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!