android study 11

Author Avatar
Xzhah 7月 14, 2022
  • 在其它设备中阅读本文章

android系统启动流程和app启动流程

1. android系统架构以及启动流程

android系统从上到下可以分为:应用层,框架层,Native层,硬件抽象层,linux内核层。

  1. loader层:当按下电源后,引导芯片会从固化在ROM中的预设代码开始执行,然后加载引导程序BootLoader到RAM中。引导程序会把系统os运行起来,并初始化一些硬件参数功能
  2. kernel层:这一层会启动linux内核,这个内核里包括了内存模型,进程调度,权限安全等服务。内核层中主要启动两个进程:swapper进程(pid=0),又叫idle进程,系统初始化kernel从无到有的第一个进程。用于初始化内存管理,进程管理,同时加载屏幕硬件,相机硬件等。kthredd进程,会创建内核中的一些线程,以及内核守护进程。内核进程的功能一般需要虚拟机去调用.
  3. 硬件抽象层:属于kernel和硬件之间的接口层,这个是为了保护厂商的知识产权。硬件厂商可以把硬件驱动放在这一层,然后去访问内核里的东西,比如控制硬件的部分。这一层会隐去具体接口实现细节,只提供接口。这些接口用来响应硬件设备的一些模块,比如蓝牙模块,WIFI模块等。
  4. Native层: 这一层分为两部分:C/C++程序库,比如SQLite,SSL,libc之类的。android运行时库,包括核心库和虚拟机。核心库就是Java语言的一些核心库功能,虚拟机就是dalvik/art。之前说了swapper/idle进程启动,然后会启动init进程,init进程就是native这一层的。init进程主要会做这些事:1)用户守护进程,用来周期性做一些事。2)启动一些重要服务,比如开机动画。3)孵化zygote进程,所有应用由zygote孵化出来。4)孵化media server进程,用来启动和管理c++ framework,比如相机服务。
  5. java框架层:主要是提供应用所需要的API。顺着上一层继续说,zygote启动后会做以下事情:1)创建服务端socket,为后续进程通信做准备。2)加载虚拟机。 3)fork system server进程,zygote fork的第一个进程,负责启动和管理java framwork层,包括ActivityManagerService,PackageManagerService,WindowManagerService、binder线程池等等。4)fork第一个应用程序-launcher。launcher是anroid系统中的Home程序,主要用来现实系统中已安装的应用程序。launcher应用程序的启动会通过请求packageManagerService返回系统中已经安装的应用信息,并将这些应用信息通过封装处理成快捷列表显示在系统屏幕上,这样用户就可以单击启动它们。

2.app应用启动流程

​ 1)launcher会首先向system_server进程(最终由进程中的activityManagerService)发送启动activity的进程间通信请求。(Binder方式)

​ 2)ams保存下来要启动的activity信息,然后给launcher发送中止状态的进程间通信请求,launcher收到后就会进入终止状态,并且给ams发送已进入终止状态的请求。(Binder方式)

​ 3)ams开始执行启动activity,如果当前进程中没有activity对应的进程,就会通过socket给zygote进程发送一个进程间通信请求,zygote会fork出一个应用进程,也就是复制一个虚拟机实例。(Socket方式)

​ 4)进程启动后,首先实例化ActivityThread,执行其main函数,创建ApplicationThread,Looper,Handler对象。并开启主线程消息循环Looper.loop()。ActivityThread main里会调用attach方法进行Binder通信,通知system_server进程执行ActivityManagerService#attachApplication(mAppThread)方法,把自己注册到ams中,用于初始化Application和Activity。

​ 5)在system_server进程中,ActivityManagerService#attachApplication(mAppThread)依次初始化Application和Activity,调用接口IApplicationThread#bindApplication接口通知主线程Handler创建Application对象,绑定Context,执行Application#onCreate生命周期函数(开发者能控制的第一行代码)。

​ 6)框架system_server进程最终是通过ActivityStackSupervisor.realStartActivityLocked()方法,最终会调用scheduleTransaction方法,通过 sendMessage 给 ActivityThead 的 Handler 发送一个 EXECUTE_TRANSACTION 消息。消息处理函数最终会调用performLaunchActivity,调用Instrumentation的callActivityOnCreate方法,启动Activity执行 Activity#onCreate() 生命周期。

2.1 zygote进程

​ 1. 把开机android系统启动过程中与zygote有关的展开详细说的话。首先native层启动的init函数具体负责以下工作:

​ 1)开机画面

​ 2)文件系统的创建和挂载

​ 3)孵化zygote进程

​ 4)启动ServiceManger,它是Binder服务管理器,管理所有android的系统服务。

  1. init进程会fork启动zygote进程,具体以下工作:

    1)创建server端的socket,负责和客户端通信

    2)预加载类和资源,提高应用启动速度

    3)启动SystemServer进程

    4)监听socket,当有应用要启动时,就会向它发出请求。

    1. SystemServer负责以下工作:

      1)启动binder线程池,这是SystemServer与其他进程沟通的方式

      2)初始化Looper

      3)创建了SystemServiceManager对象,它会启动Android中的各种服务。包括AMS、PMS、WMS

      4)启动桌面进程,这样才能让用户见到手机的界面。

      5)开启loop循环,开启消息循环,SystemServer进程一直运行,保障其他应用程序的正常运行。

2.2 android binder机制

​ AMS和ActivityThread主要通过进程间通信。AMS 会通过 Application 来和 ActivityThread 进行进程间通信。跨进程通信的机制就是把数据从本地进程和空间地址传输至远程空间和地址。Binder的好处是只用复制一次,性能高于消息队列等其他方式,其本质是两个进程不同的逻辑地址对应相同的物理地址。

​ App进程与SystemServer进程通过Binder机制进行进程间通信,Android中有两个相关的接口:

​ 1)IApplicationThread: 作为系统进程请求应用进程的接口

​ 2)IActivityManager: 作为应用进程请求系统进程的接口

​ 对于一个Binder接口,在客户端和服务端各有一个实现,Native和Proxy,它们之间的通信通过transcat和onTranscat触发,Nativexxx就是自身进程的Binder代理类,Proxy是远程进程的Binder代理类。

​ 如之前所说,这些Binder都是由ServiceManger管理(init进程孵化而来),ServiceManger管理android所有的系统服务,可以认为ServiceManager是Binder机制里的DNS服务器,客户端如果想使用某个服务,调用相应的getSystemService接口,ServiceManager就会通过Binder名称找到并且返回对应服务的Binder对象。

2.3 服务端: IActivityManager

​ ActivityManagerNative作为服务端的“桩(Stub)”,其主要职责就是对远程传递过来的数据进行反序列化;

​ ActivityManagerProxy作为服务的“代理(Proxy)”,运行在客户端,其主要职责就是将数据进行序列化,再传递给远程的“桩(Stub)”

​ App使用AMS提供的功能,比如startActivity,是通过AMS在客户端的代理ActivityManagerProxy发起的。

​ 最下面一层是桩(Stub)的具体实现——AMS(ActivityManagerService),负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作。

2.4 客户端:IApplicationThread

​ 桩(Stub):ApplicationThreadNative

​ 代理(Proxy):ApplicationThreadProxy,App在客户端进程中实现了实例化Activity、调用onCreate等生命周期函数的功能,因为跨进程也不能被AMS直接调用,而是AMS通过客户端的代理ApplicationThreadProxy来处理。

​ 最下面一层是桩(Stub)的具体实现——ApplicationThread,它是ActivityThread的一个内部类,ApplicationThread负责响应系统进程发起的请求,而实际触发的业务逻辑是在ActivityThread中。与一般的代理模式不同,它不是直接持有ActivityThead的一个引用,而是把处理的请求发到ActivityThread内部的一个Handler上。

​ ActivityThread是应用启动C/S模式中的客户端,包含了应用进程主线程的全部机制:1.启动应用的主线程,开启消息循环。2. 提供了一个IActivityThread接口作为与AMS通讯接口,通过该AMS将Activity的状态变化传递到客户端的Activity对象。

2.5 Application的初始化

​ 在ActivityThread创建的时候,会将自己的ApplicationThread绑定到AMS中。ApplicationThread是 ActivityThread 的一个内部类,是一个 Binder 对象。上面说到AMS#attachApplication会初始化Application和Activity,这个过程具体如下:

​ 1)在ActivityThread创建的时候,会将自己的ApplicationThread绑定到AMS中。应用进程作为客户端通过IActivityManager接口发起跨进程调用,这样AMS#attachApplication会被执行。该函数会把应用进程传过来的IApplication实例保存在系统进程维护的ProcessRecord中,这样一来AMS后续可以通过IApplicationThread实例发起应用进程的调用。

​ 2)在attachApplication过程中,会有一些信息要传递给应用进程,以便应用进程的初始化,如解析AndroidManifest.xml得到的数据结构ApplicationInfo,这时候就会通过IApplication接口的bindApplication来通知主线程创建Application对象,绑定Context,调用Application.onCreate。

2.6 Activity的初始化

ActivityManagerService#attachApplication(mAppThread)里依次初始化了Application和Activity,其中的mStackSupervisor#attachApplicationLocked(ProcessRecord)里进行了Activity的初始化。

​ AMS通过ActivityRecord来维护Activity运行时的状态信息,需要将Activity绑定到AMS中的ActivityRecord。在Activity类中有一个IBinder类型的属性:private IBinder mToken;,IBinder类型表示这个属性是一个远程对象的引用,Token持有了一个ActivityRecord实例的弱引用。在创建一个ActivityRecord的时候,就会创建了一个Token类型的对象。

​ 系统进程维护ActivityRecord,应用进程维护Activity,两者之间的映射关系就是利用Token来维系。在发生Activity切换时,应用进程会将上一个Activity的Token(AMS.startActivity()的输入参数resultTo)传递给系统进程,系统进程会根据这个Token找到ActivityRecord,对其完成调度后,再通知应用进程:Activity状态发生了变化。

2.7 Binder机制

​ Binder用于进程间通信,但linux内核已经有了管道,消息队列,共享内存,Socket等IPC机制,那为什么android还要提供Binder?原因是从性能,安全性,稳定性三个方面考虑。

​ 1)性能,一句话来说Binder由于只需要一次数据拷贝,所以性能上占据优势,仅次于共享内存。Socket是一款通用接口,其传输效率低,开销大,主要用在跨网络的进程间通信和本机进程间低速通信。而管道和消息队列采用的是存储-转发模式,即先把数据从发送方缓存区拷贝到内核缓存区,然后接收方再从内核缓存区拷贝到自己的进程缓存区。这期间就发生了两次拷贝。共享内存虽然无需拷贝,但是控制复杂,难以使用。Binder只需要把数据拷贝到内核缓存区后,给内核缓存区以及接收进程用户空间之间建立映射关系(不同逻辑地址对应相同物理地址),这样无需第二次拷贝,接收进程也就可以访问到发送的数据了。

​ 2)稳定性,Binder是基于C/S架构,客户端有什么需求就丢给服务端完成,大家各司其职,互相独立。所以从稳定性上来说,Binder是优于共享内存的。

​ 3)安全性,传统IPC无法获得对方可靠的UID/PID,无法鉴别身份,因为传统IPC是由用户在数据包中填入PID/UID。其次传统IPC访问接入点是开放的,只要知道接入点程序就可以建立连接。而Binder 驱动程序将发送者进程的 UID 和 PID 添加到每个事务。 因此,由于系统中的每个应用具有其自己的 UID,所以该值可以用于识别调用方。 调用的接收者可以检查所获得的值并且决定是否应该完成事务。

2.8 Binder通信模型

​ 这个通信模型中一共有四个参与者:Client,Server,ServiceManager/Binder驱动。其中只有Binder驱动运行在内核空间,其他都在用户空间。ServiceManager和Binder驱动由系统提供,Client和Server由程序提供。

Binder驱动

​ Binder驱动如同路由器一样,是整个通信的核心,驱动负责进程之间Binder通信的建立,Binder的传递,计数管理,数据包在进程之间的交互等一系列底层支持。

ServiceManager与实名Binder

​ ServiceManager与DNS类似,作用是将字符串形式Binder名字转成该Binder的实体引用。注册了名字的Binder叫实名Binder, Server在创建Binder后,会为他起一个字符串形式,可读易记的名字。将这个Binder实体连同名字一起通过Binder驱动发送给ServiceManager。ServiceManager会维护相关信息。

​ ServiceManager与Client/Service通信也是通过Binder, ServiceManager是服务端,他也有Binder实体,且这个实体不需要注册也没有名字。当一个进程使用 BINDERSETCONTEXT_MGR 命令将自己注册成 ServiceManager 时 Binder 驱动会自动为它创建 Binder 实体。且这个Binder在其他进程中都是0号引用。

​ Client通过0号Binder向ServiceManager通过名称获取Binder实体。

references

https://juejin.cn/post/6844904116561379341

https://www.jianshu.com/p/37370c1d17fc

https://www.jianshu.com/p/c95c208774c2

Binder学习指南 | Weishu’s Notes