写了这么久的Android应用程序,是时候来八一八Android程序的启动原理了,文章不长,喜欢的朋友点个收藏谢谢。

只要是Android系统,运行的第一个程序一定是引导程序,可以这么说,所有的unix系统都有引导加载程序。这个引导加载程序的作用是在在加载 Linux 内核之前进行低级(底层)系统初始化。

Android系统启动的第一阶段是将recovery镜像加载到系统flash里(就是我们的固定内存Rom),这也是引导程序的主要功能。

引导程序会检测手机按键(实体按键,刷机的小伙伴不陌生,recovery模式下有些不能用触屏,要使用方向键和关机键配合操作),我们可以用这些案件切换功能和模式,比如快速启动或实现某个功能,比如重新刷写镜像,下载和执行内核image。

android 启动页和引导页 安卓启动引导程序_Android


最终的效果就是会把内核加载到RAM中(这里通常是/boot flash分区中的内核)。Linux内核

android 启动页和引导页 安卓启动引导程序_Android_02

内核在启动构建Android的用户空间程序和应用程序之前,会完成大部分硬件,驱动程序和文件系统的初始化,只要包括:

  • 核心内核初始化(内存和I/O区初始化,中断启动,进程表初始化)
  • 驱动程序初始化
  • 挂载/root文件系统
  • 启动第一个用户空间进程init(我之前写过一篇init.rc加载的文章,那个是脚本,这个是进程)

android 启动页和引导页 安卓启动引导程序_android_03


在启动时,Android回家再Linux内核并启用进程号为1(pid=1)的名叫init的进程,这个和其他linux系统的启动是一样的,我们可以通过ps打印出来:

android 启动页和引导页 安卓启动引导程序_unix_04

init程序是 Android 启动序列的关键组件,它是初始化 Android 系统的主要元素。

Android.init会处理两个文件,执行它里面的脚本。

第一个是通用的init.rc,有兴趣的小伙伴可以去看下那篇文章,都有中文注释,看了大概能搞明白init启动过程。这里我们简单过一下。init.rc适用于所有的Android设备,每个版本的init.rc文件内容会有一些区别。第二个rc文件是根据设备厂商来的,比如nexus7的rc文件名称是init.flo.rc

android 启动页和引导页 安卓启动引导程序_linux_05


在这些文件中,我们可以找到负责启动名为daemons的进程的命令。它们位于硬件抽象层之上,然后通过sockets监听一些数据,比如下面的:

  • ADBD 全名叫做Android Debug Bridge Daemon,用于管理Adb连接。
  • Debugger Daemon管理调试程序请求,比如dump内存等。
  • Radio Interface Layer Daemon这个我们见得不多,因为他是跟基带调试调节器相关的,用于管理无线电通讯的,手机必须有这个才能称作电话。

然后他还启动了系统服务,这个服务很重要,我们在framework层申请的所有服务都要通过这个服务来申请,我们暂且理解为所有服务的老大哥。

android 启动页和引导页 安卓启动引导程序_android 启动页和引导页_06


然后你往下看还能找到决定开机动画的代码,你知道我们其实可以通过adb shell随时调用开机动画吗?

adb shell bootanimation

然后你就会看到这个:

android 启动页和引导页 安卓启动引导程序_linux_07

启动 Linux 守护进程后,init 进程启动了一个非常简洁的进程,称为Zygote。(做逆向的肯定不陌生,因为附加进程都是在这个孵化进程执行的时候)。这只是 Android 平台所有其余部分的开始,Zygote 进程基本上是一个预热的ART/Dalvik VM,加载了所有标准核心包(如 java.util.HashMap ,j包括java包下mac地址,zygote孵化出应用的时候都会用到这些核心标准组件,这也就是为什么不能在程序启动后通过hook修改真实mac地址的原因)。

zygote加载后它会睡眠状态。当需要启动新的应用程序时,系统会向该进程发送一个 Intent(带有类似于“执行 com.example.MainActivity”的消息)。Zygote 进程被唤醒,fork一个进程子进程 ,创建一个新的 VM 实例并加载请求的组件,例如。com.example.MainActivity),然后重新进入睡眠状态。

所以每个应用程序都运行在自己的进程中,互不干扰。

Zygote 是 Android 应用程序启动如此之快的原因。

此时我们已经启动了一堆子流程:

android 启动页和引导页 安卓启动引导程序_linux_08

Init 启动运行时进程

Runtime 是AppRuntime的一个实例,他是AndroidRuntime的子类。

Runtime这个进程做了一件非常重要的事情:

它启动了SystemManager(服务管理器),这实际上是Binder 的上下文管理器,用于处理服务注册和查找(如 DNS服务)。

同时它将 Service Manager 注册为 Binder 服务的默认上下文管理器。

android 启动页和引导页 安卓启动引导程序_android_09


接下来,Zygote fork 一个新的 ART/Dalvik VM 实例并启动System Server。

System Server 用来启动像 Surface Flinger、Audio Flinder 一样的系统服务,例如:

  • Entropy Service
  • Power Manager
  • Activity Manager
  • Telephony Registry
  • Package Manager
  • Battery Service
  • Lights Service
  • Vibrator Service
  • Alarm Manager
  • Window Manager
  • Bluetooth Service
  • 以及其他的一些服务。

可以点击这里看源码实现;

上面这些服务都会把自己注册到ServiceManager中。

android 启动页和引导页 安卓启动引导程序_unix_10


如果要使用这些服务,你只需要这么调用:

val powerManager = context.getSystemService(Context.POWER_SERVICE)

是不是贼方便?

System准备好了

到这个时候你就可以启动home应用了,比如launcher。

Activity Manager会发送一个请求到Zygote然后告诉他“我现在要启动一个activity并且订阅的filter是”Intent.ACTION_MAIN 和 Intent.CATEGORY_HOME,这时会触发Zygote执行fork操作并使用Home Activity 和新的 ART/Dalvik VM 加载这个新进程。

android 启动页和引导页 安卓启动引导程序_android_11


这个时候你已经进入到系统桌面了。

android 启动页和引导页 安卓启动引导程序_linux_12


接下来我们来看看,我们开发的your.app.MainActivity是怎么被启动的呢?

android 启动页和引导页 安卓启动引导程序_linux_13

  1. 点击应用程序图标。Home应用程序(Launcer)会执行下面代码:
val launchIntent = packageManager.launchIntentForPackage("your.app.MainActivity") 
startActivity(launchIntent)
  1. 在 PackageManager 和 ActivityManager 的配合下使用你的包名your.app/your.app.MainActivity调用 Zygote,你可以把它和命令行执行java类比:
java -cp your.app your.app.Main
  1. zygote 然后使用新的 ART/Dalvik VM fork 并加载活动组件your.app.MainActivity。

    到这里整个系统启动并启用应用程序就分析完了,如果你觉得有帮助,欢迎点赞收藏让更多的人看到哦。