Android 系统的启动流程
Android 系统的启动流程可以分为以下几个主要步骤:
-
引导加载器(Bootloader)启动:当你打开一个 Android 设备时,首先启动的是引导加载器。引导加载器负责启动 Android 的核心操作系统。
-
Linux 内核启动:引导加载器加载并启动 Linux 内核。Linux 内核负责启动系统的底层硬件,并启动 init 进程。
-
Init 进程启动:init 进程是 Linux 系统中的第一个进程(PID = 1)。在 Android 系统中,init 进程负责启动一些重要的守护进程和服务,例如 Zygote 进程。
-
Zygote 进程启动:Zygote 是 Android 中的一个特殊进程,负责孵化(Fork)出所有其他的应用进程。Zygote 进程在启动时会预加载大量的系统类和资源,以此来提高应用启动的速度。
-
SystemServer 进程启动:Zygote 进程会 fork 出一个新的进程来启动 SystemServer。SystemServer 是 Android 系统中的核心服务,它管理并提供了许多系统级别的服务,例如窗口管理服务、活动管理服务等。
-
系统服务启动:在 SystemServer 进程中,会启动各种系统服务。这些服务在系统启动完成后,将一直运行,为应用程序提供各种服务。
-
启动 Launcher 应用:系统服务启动完毕后,Activity Manager Service 会启动设备的 Launcher 应用,此时,用户可以开始与设备进行交互。
这就是 Android 系统启动的整个过程。请注意,这个过程可能会因设备制造商的定制和 Android 版本的不同而有所不同,但大体流程是相同的。
Launcher的启动流程
Launcher 的启动过程更具体的说明如下:
-
当 Android 系统启动完成,并且所有的系统服务都已经启动后,ActivityManagerService (AMS)会开始处理用户的交互。AMS 是 Android 中负责管理所有活动的系统服务。
-
AMS 会发送一个包含 ACTION_MAIN 和 CATEGORY_HOME 的 Intent。这个 Intent 的目的是寻找并启动设备上配置为主屏幕(HOME)的应用,通常这就是 Launcher 应用。
-
这个 Intent 会通过 Android 的 Intent 解析系统,寻找所有能够响应这个 Intent 的应用。由于 Launcher 在其清单文件(AndroidManifest.xml)中声明了相应的 ,因此它会被选中来响应这个 Intent。
-
一旦 Launcher 被选中,AMS 就会请求 Launcher 启动。如果这是设备第一次启动,或者 Launcher 进程尚未启动,那么 AMS 会请求 Zygote 启动一个新的进程来托管 Launcher。如果 Launcher 进程已经存在,AMS 就会在该进程中启动新的 Launcher 活动实例。
-
当 Launcher 启动后,它会创建并显示用户的主屏幕。这个屏幕上会显示应用的图标、小部件等,用户可以通过这个界面启动和切换应用。
startActivity 详细说明
-
发送 Intent:源应用程序(App1)调用 startActivity 方法,传递一个 Intent。该 Intent 包含要启动的目标应用程序(App2)的 Activity 的信息。
-
验证权限和 Intent:在 ActivityManagerService (AMS) 中,startActivity 方法会对 Intent 进行检查和验证。这包括验证调用者的权限,查找 Intent 对应的 Activity,处理各种启动模式等。
-
判断目标应用程序进程状态:接着,AMS 会判断目标应用程序 App2 的进程是否已经存在。如果已经存在,AMS 会使用已经存在的进程;如果不存在,AMS 会向 Zygote 发送请求,要求它创建一个新的进程。
-
创建新的应用程序进程:Zygote 在收到 AMS 的请求后,会创建一个新的应用程序进程,然后在这个进程中创建一个 Application 对象,并执行其 onCreate 方法。
-
创建 ActivityRecord:与此同时,AMS 会创建一个新的 ActivityRecord 对象,来代表要启动的 Activity。
-
调度启动 Activity:然后,AMS 会通过 Binder IPC 向新的应用程序进程发送一个 LAUNCH_ACTIVITY 消息。
-
接收 LAUNCH_ACTIVITY 消息:新的应用程序进程在接收到 LAUNCH_ACTIVITY 消息后,会创建目标 Activity 的实例,然后调用其 onCreate、onStart 和 onResume 方法。
ActivityManagerService 启动
**ActivityManagerService (AMS) **是 Android 中最为关键的系统服务之一,它负责管理系统中的进程,以及四大组件(Activity、Service、BroadcastReceiver 和 ContentProvider)的生命周期。这里将详细解释 AMS 的内部启动流程和工作流程:
AMS的启动流程:
实例化:AMS 是在 SystemServer 中被实例化的。SystemServer 会首先创建一个 ContextImpl 对象,然后利用这个 ContextImpl 创建 AMS。
服务初始化:接下来,调用 AMS 的 init 方法进行初始化,这个方法会创建和初始化一些需要的数据结构和服务,比如 BroadcastQueue、IntentFirewall 和 ActivityStackSupervisor 等。
准备完成:完成初始化后,调用 systemReady 方法来通知 AMS 系统准备就绪。在这个方法中,AMS 会完成一些后期初始化的工作,例如启动系统的 Home Activity。
AMS的工作流程:
AMS 主要的工作就是管理系统中的四大组件和进程。以下是它们分别的工作流程:
Activity的管理:AMS 使用 ActivityStackSupervisor 来管理系统中所有的 Activity。当应用程序调用 startActivity 方法时,会通过 Binder IPC 机制调用 AMS 中的 startActivity 方法。AMS 会根据 Intent 解析出需要启动的 Activity,创建一个 ActivityRecord 对象并添加到 ActivityStack 中。然后,AMS 会向应用程序发送 LAUNCH_ACTIVITY 消息,告诉应用程序启动新的 Activity。
Service的管理:当应用程序调用 startService 或 bindService 方法时,AMS 会创建一个 ServiceRecord 对象来保存 Service 的状态。然后,AMS 会向应用程序发送 CREATE_SERVICE 或 BIND_SERVICE 消息,告诉应用程序创建或绑定 Service。
BroadcastReceiver的管理:当应用程序调用 sendBroadcast 方法时,AMS 会创建一个 BroadcastRecord 对象,保存广播的信息。然后,AMS 会将 BroadcastRecord 添加到 BroadcastQueue 中,等待处理。
ContentProvider的管理:当应用程序调用 getContentResolver().query() 或其他方法时,AMS 会查找对应的 ContentProviderRecord,然后通过 Binder IPC 机制向目标 ContentProvider 发送请求。
进程的管理:AMS 使用 ProcessList 和 ProcessRecord 来管理系统中的进程。当需要创建新的进程时,AMS 会向 Zygote 发送请求,然后由 Zygote 创建新的进程。同时,AMS 也负责进程的调度和回收。
主要信息
startProcessLocked
在 Android 中,startProcessLocked() 方法主要是在 ActivityManagerService 中,用于启动新的应用进程。这个方法会被系统在需要启动新的应用进程时调用,例如,当用户启动一个新的应用或者服务需要在新的进程中运行时。以下是一些主要的步骤和操作:
检查是否已经存在相同的进程:首先,系统会检查是否已经存在一个相同名字的进程。如果已经存在,则不会再创建新的进程。
检查系统资源限制:系统会检查当前的系统资源,如内存使用情况等,来决定是否有足够的资源启动新的进程。
创建进程记录:如果可以启动新的进程,系统会创建一个新的 ProcessRecord 对象来保存新进程的信息。
启动新进程:使用 Zygote 启动新进程。Zygote 是 Android 中的一个特殊进程,它预加载了大量的系统资源和常用库,新的应用进程会通过 fork Zygote 进程的方式启动,这样可以提高进程启动的速度。
设置进程的参数:系统会设置新进程的各种参数,例如进程的优先级、所属用户等。
更新系统状态:系统会更新当前的系统状态,例如更新当前运行的进程列表等。
runOnce
**runOnce()**方法在Android的Zygote进程中非常关键,它是处理进程启动请求的主要逻辑。
在Android系统中,新的应用进程是通过fork Zygote 进程来创建的。具体来说,当需要启动新的应用进程时,系统会通过一个特殊的socket 与Zygote 进程进行通信,向Zygote 进程发送一个包含新进程需要的信息的请求,Zygote进程在接收到请求后,会执行**runOnce()**方法来处理这个请求。
以下是runOnce()方法的主要步骤:
读取请求参数 :runOnce()方法首先会从Zygote socket中读取请求参数,这些参数包括新进程的一些信息,例如应用的包名、启动类名等。
fork新进程 :读取完请求参数后,runOnce()方法会调用 forkAndSpecialize()或 forkSystemServer()方法来fork 一个新的进程。这两个方法的主要区别在于,forkAndSpecialize()方法是用于创建普通的应用进程,而 forkSystemServer()方法是用于创建System Server进程。
初始化新进程 :fork新进程后,**runOnce()**方法会对新进程进行一些初始化操作,例如设置新进程的环境变量、加载应用的dex文件等。
启动新进程的主类 :初始化完成后,runOnce()方法会调用 RuntimeInit.zygoteInit()方法来启动新进程的主类。对于普通的应用进程,主类通常是应用的Application 类;对于System Server 进程,主类是com.android.server.SystemServer类。
返回新进程的PID:最后,**runOnce()**方法会将新进程的PID返回给系统。这个PID是新进程的唯一标识,系统可以使用这个PID来管理新进程。
ActivityThread的main函数
ActivityThread 是 Android 中的一个非常重要的类,它是每个 Android 应用程序主线程的入口点,也就是应用程序的主线程("main" thread)。在 ActivityThread 的 main 方法中,执行了一系列的初始化操作来启动和设置应用程序。
以下是 ActivityThread 的 main 方法主要做了哪些事情:
设置 Thread 属性:主线程的名称会被设置为** "main"**。
创建 Looper :在 Android 中,主线程需要一个 Looper 对象来处理消息队列。Looper.prepareMainLooper() 方法会创建一个绑定到主线程的 Looper 对象。
创建 ActivityThread 对象 :创建 ActivityThread 实例并调用其 attach 方法,这个方法会完成一些重要的初始化工作,包括设置应用的上下文,创建应用的 Instrumentation 对象等。
设置 Android 默认的异常处理器:如果应用抛出未捕获的异常,这个处理器将会被调用。
开始循环 :通过调用 Looper.loop() 来开始主线程的消息循环。
在应用进程启动后,需要向 ActivityManagerService (AMS) 报告是因为 AMS 是 Android 系统中管理所有应用程序生命周期的关键组件。当一个新的应用程序进程被创建时,它需要告诉 AMS 它已经启动并且准备好接收指令。此外,这也是一个错误检查机制:如果 AMS 启动了一个进程,但是这个进程在一段时间内没有报告回来,那么 AMS 会认为这个进程启动失败,会采取相应的操作,如重新启动这个进程或者报告一个错误。
启动binder机制
Binder 机制主要在应用程序启动时进行初始化,并在其进程生命周期内保持活动状态,用于处理进程间通信(IPC )的请求。关于 Binder 机制的启动,这主要发生在应用程序的主线程(ActivityThread)初始化过程中。
以下是 Binder 机制的启动过程:
创建应用的主线程 :每个** Android** 应用都有一个主线程,它是应用程序的入口点。当新的应用程序进程创建时,Zygote 会复制其内存空间,并在新的进程空间中启动应用的主线程。主线程的入口函数是 ActivityThread.main()。
初始化 Looper :在 ActivityThread.main() 函数中,会调用 Looper.prepareMainLooper() 来为主线程创建一个 Looper 。Looper 是一个用于处理消息队列的工具,是实现事件驱动编程的关键组件。
创建 ActivityThread 对象 :接下来,会创建一个 ActivityThread 对象,并调用其 attach() 方法来进行一些必要的初始化工作。在这个过程中,会创建应用的 Context ,Instrumentation 对象等。
创建 Binder 线程池 :在 ActivityThread 的 attach() 方法中,会调用 BinderInternal.addGcWatcher() 和 BinderInternal.createBinderThreadPool() 来为当前进程创建 Binder 线程池。
addGcWatcher() 方法会添加一个 GC (垃圾回收)监听器,该监听器会在每次完成 Binder 事务后执行一次 GC ,以防止 Binder 事务造成的内存泄漏。
createBinderThreadPool() 方法会创建一个 Binder 线程池。这个线程池会在后台运行,用于处理来自其他进程的 Binder 请求。
启动消息循环 :最后,主线程的 Looper 对象会调用其 loop() 方法来启动消息循环。在这个消息循环中,主线程会不断地从消息队列中取出消息并处理,包括处理来自 Binder 线程池的消息。
Binder Native 层
打开 Binder 驱动 :在 Android 系统启动的过程中,会加载 Binder 驱动。在每个进程创建的过程中,会打开 /dev/binder 设备,这样,该进程就可以使用 Binder 驱动进行 IPC 通信了。
映射内存 :当进程打开 /dev/binder 设备后,会通过 mmap 系统调用映射一块内存空间作为进程和 Binder 驱动之间的通信区域。这样,进程和 Binder 驱动就可以通过这块共享内存进行数据交换,提高了通信效率。
创建 Binder 线程池 :每个进程都需要创建一个 Binder 线程池来处理来自其他进程的 Binder 请求。Binder 线程池是一个由多个线程组成的线程池,每个线程都会使用 ioctl 系统调用进入到 Binder 驱动的等待队列,等待来自其他进程的 Binder 请求。
当一个进程通过 Binder 机制向另一个进程发送请求时,Binder 驱动会唤醒等待队列中的一个线程来处理这个请求。这个线程会读取共享内存中的数据,处理请求,并将结果写回到共享内存中。然后,这个线程会再次进入到 Binder 驱动的等待队列,等待下一次的请求。
冷启动 App 全流程
- 点击桌面图标后,Launcher 应用会构建一个 intent 并发送给 AMS,通知 AMS 启动 App;
- AMS 收到 intent 消息后,首先做一些校验,然后判断 App 对应的进程是否启动,如果没有启动,则通知 Zygote 进程启动 App;
- AMS 通过 socket 与 Zygote 进程通信,将必要的启动参数传递过去,Zygote 进程收到后,通过 fork 调用创建新的进程,并且在主进程中将子进程的 pid 返回给 AMS 所在的进程;
- fork 出来的进程也就是 App 进程,进程启动后首先会初始化 Binder 机制,具体为打开 Binder 驱动、通过 mmap 映射内存缓存,启动 binder 线程池;
- App 进程在 Binder 启动后会创建一个 ApplicationThread 的 Binder 实体,通过ServerManager 获取 AMS 的 Binder 代理,将 ApplicationThread 这个 Binder 传递给 AMS;
- AMS 收到 attach 后,保存 ApplicationThread 对象的 Binder 代理对象,以方便后续与 App 进程进行通信。AMS 发送 attachApplication 请求,该请求最后会在 App 所在进程的 Binder 线程被执行,然后构建一个 Message 对象发送给主线程执行 Application 对象的初始化;
- App 的主线程收到消息后,构建 LoadedApk 对象,该对象存储 App 的一些基本信息,紧接着创建 ContentImpl 对象,并且通过反射创建 Application 对象,接着调用其 attach 方法,传入 ContentImpl 对象,最后调用其 onCreate 方法;
- 再回到 AMS 刚刚通知 App 创建 Application 的位置,AMS 在执行完该事后,会立马通知 App 启动 launch类型的 Activity 以及一些 service、广播等
- App 在收到启动首页 Activity 事件后,通过反射创建 Activity 对象,并且同时创建一个 ContentImpl 对象,接着执行 Activity 的 onCreate 方法,设置布局文件;
- 接下来 onResume 方法中,通过 WMS 的 addView 方法,将 ViewRoot 中的视图添加到 WMS 中,并且视图树会执行 attachToWindow 回调;
- ViewRootImpl 会通过 Choreographer 注册 vsyn 信息,在下一个 vsyn 信号到来之后,会执行 msasure、layout、draw ,在 draw 方法执行完之后,其内部通过 canvas 的 api 调用绘制指令,这些指令会被记录下来,最后通过 RenderThread 线程进行计算执行,最终通过 GPU进行渲染;
- 渲染的数据会放到 Surface 缓冲区中,SufaceFlinger 将所有的窗口对象的 Surface 数据区域进行合成,并发送到屏幕的后缓冲区域;
- 下一个 vsync 信号到来之后,后缓冲区的图像就会显示到屏幕上;