Android 系统及应用启动流程

一、系统启动流程

Android 是一个基于 Linux 的系统,在按下电源键的那一刻,系统便启动了,那么在按下电源键之后,系统是如何启动的呢?

带着这个问题去探究可以知道,在底层中电源键按下后,引导芯片将开始从预定义的地方开始执行,加载引导程序到 RAM,进而执行引导程序,一般引导程序为针对主板和芯片的预定义程序,引导程序也是OEM厂商或者运营商加锁和限制的地方。

此外会初始化内核所需要的比如网络、内存等,此后将会启动内核,由于 Android 是基于 Linux 内核开发,所以整个的内核启动过程与 Linux 类似,尚未了解,不敢妄言。

在内核启动完成后,便会创建第一个进程,便是 init 进程。

1.1 init 进程

  • 在 Linux 启动之后,init 是用户空间里的第一个进程,确切的说 init 是 Linux 用户空间的第一个进程,但由于 Android 也是基于 Linux 内核,所以 init 也是 Android 系统中用户空间的第一个进程。

  • 关于用户空间的概念:在 Linux 中,操作系统和驱动程序运行在内核空间,而应用程序运行在用户空间。两者之间不能简单的使用指针传递数据,因为 Linux 中使用的是虚拟内存机制,用户空间的数据可能被换出,所以当内核空间通过用户空间的指针时,可能会导致对应的数据丢失,所以会采用段页式地址映射机制。

ps:对于底层实现,由于知识储备不到位,所以很多一知半解,暂时不做分析 T_T..

  • init 进程负责创建系统中的几个关键进程,例如 log 系统,在此处要注意的是 zygote 进程,zygote 进程是整个 Java 世界的父进程,是后面 Android 系统启动流程所关注的重点。

1.2 zygote

1.2.1 Native 层
  • 在 Android 的系统架构层级划分中,一般分为四层:应用层、framework 层、Native 层、Linux 内核,在这四层中,其中应用层和 framework 层为 Java 实现,Native 为 C/C++ 的程序,而 zygote 就是所有 Java 进程的最初形态,zygote 这个词中文意思为“受精卵”,更可以形象的描述出 zygote 在整个 Java 世界的作用。

  • zygote 本身是一个 Native 的程序,源码中其入口在

/platform_frameworks_base/cmds/app_process/App_main.cpp

1
2
3
4
5
6
7
8
9
....
int main(int argc, char* const argv[])
{
....
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
....
}

在程序中可以看到, zygote 中的 main 函数中主要的功能均由 AppRuntime 完成,此后的活动也均由 AppRuntime 控制,所以在

/platform_frameworks_base/core/jni/AndroidRuntime.cpp

下找到 AndroidRuntime.cpp 文件,在其中看到 AppRuntime 重载了 onStarted、onZygoteInit 和 onExit 函数,由于 C++ 水平有限,所以仅对整个流程做一个梳理,不做具体实现分析,在整个的实现过程中,可以看到通过 startVm 调用 JNI 虚拟机的创建函数,在 startReg 中注册 JNI 函数,要知道 JNI 是连接 Java 和 Native 的桥梁,至此完成了虚拟机的创建,所谓虚拟机,在 Linux 下就是一个进程,Android 中每一个程序都有一个单独的进程,此后,通过 CallStaticVoidMethod 方法,将进入到 Java 层面。

1.2.2 Java 层
  • 以下内容所涉及到的源码文件:

/platform_frameworks_base/core/java/com/android/internal/os/

  • ZygoteInit.java
  • Zygote.java
  • ZygoteConnection.java

/platform_frameworks_base/core/java/android/net/LocalServerSocket.java
/system/core/libutils/Threads.cpp

  • 作为 Java 世界的入口,通过 CallStaticVoidMethod 方法,最终将调用 ZygoteInit.java 的 main 函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public static void main(String argv[]) {
.......
try {
RuntimeInit.enableDdms(); //开启DDMS功能
SamplingProfilerIntegration.start();
boolean startSystemServer = false;
String socketName = "zygote";
String abiList = null;
for (int i = 1; i < argv.length; i++) {
if ("start-system-server".equals(argv[i])) {
startSystemServer = true;
} else if (argv[i].startsWith(ABI_LIST_ARG)) {
abiList = argv[i].substring(ABI_LIST_ARG.length());
} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
socketName = argv[i].substring(SOCKET_NAME_ARG.length());
} else {
throw new RuntimeException("Unknown command line argument: " + argv[i]);
}
}
...
registerZygoteSocket(socketName); //为Zygote注册socket
preload(); // 预加载类和资源
SamplingProfilerIntegration.writeZygoteSnapshot();
gcAndFinalize(); //GC操作
if (startSystemServer) {
startSystemServer(abiList, socketName);//启动system_server
}
runSelectLoop(abiList); //进入循环模式
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
caller.run(); //启动system_server
} catch (RuntimeException ex) {
closeServerSocket();
throw ex;
}
}
}
........

在上面的 main 函数里,几个主要的函数:

  • registerZygoteSocket — 建立 IPC 通信服务端

由于 zygote 在与其他进程的通信中没有使用到 Binder 机制,而是采用了基于 AF_UNIX 类型的 Socket。

Tips: ( 格式化套接口地址,相应的,地址族用来指明哪种类型的地址,常量AF_LOCAL(AF_UNIX)指明了地址将会按照本 地(UNIX)地址规则来格式化。常量AF_INET指明了地址将会符合IP地址规则。在一个地址族中,可以有多种类型 )

在这里,其实建立起的是一个服务端的 Socket ,进而可以在后续进行 IPC 通信。

  • preload — 预加载资源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static void preload(BootTimingsTraceLog bootTimingsTraceLog) {
.....
// 预加载位于/system/etc/preloaded-classes文件中的类
preloadClasses();
// 预加载资源,包含drawable和color资源
preloadResources();
// 预加载OpenGL
preloadOpenGL();
// 通过System.loadLibrary()方法,
//预加载"android","compiler_rt","jnigraphics"这3个共享库
preloadSharedLibraries();
// 预加载 文本连接符资源
preloadTextResources();
}

在 preload() 方法中,preloadClasses() 会预加载的预加载类的列表在 /platform_frameworks_base/tools/preload 下,会进行加载时间的判断,当每个类的加载时间大于 1250 微秒的时候,就会被 zygote 预加载,所以在这里,也是一个优化系统启动速度的优化点,但是对技术的要求比较高~

同时这里还有一个小问题,就是当创建新的进程的时候,这些预加载的类和资源应该怎么操作呢? 这里的处理方案采用了 copy on write 技术来实现,关于写时复制技术原理,暂不做细述。

  • startSystemServer — 启动 System_server

在这个方法中,通过设置参数和 fork 的方式,zygote 实现了一次分裂,分裂出一个 system_server 进程,即代码中对应 Zygote.forkSystemServer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
private static boolean startSystemServer(String abiList, String socketName)
throws MethodAndArgsCaller, RuntimeException {
long capabilities = posixCapabilitiesAsBits(
......
);
// fork 参数准备
String args[] = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1032,3001,3002,3003,3006,3007",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
"com.android.server.SystemServer",
};
ZygoteConnection.Arguments parsedArgs = null;
int pid;
try {
// 用于解析参数,生成目标格式
parsedArgs = new ZygoteConnection.Arguments(args);
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
// fork 子进程,运行 system_server
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids,
parsedArgs.debugFlags,
null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
//进入子进程system_server
if (pid == 0) {
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
// 完成system_server进程剩余的工作
handleSystemServerProcess(parsedArgs);
}
return true;
}
  • runSelectLoopMode — 开启服务端等待请求

在 zygote 从startSystemServer 返回之后,进入到下一个关键函数:runSelectLoopMode 。在 registerZygoteSocket 中我们注册了一个用户 IPC 通信的 Socket,但是当时并没有启用,而它真正启用的地方便是在 runSelectLoopMode 中,并且在这里 zygote 采用的是高效的 I/O 多路复用机制,保证在没有客户端请求和数据处理的时候休眠,在客户端请求时响应处理。

zygote 启动流程图

1.2.3 zygote 小结

虽然只是系统启动流程了解,但是到现在结合上面的流程图,总结一波在这里 zygote 究竟干了什么:

  1. 创建 AppRuntime 对象并调用它的 start 方法,启动 zygote 进程,此后的操作活动由 AppRuntime 来控制。
  2. 调用 startVm 创建 Java 虚拟机,调用 startReg 来注册 JNI 函数。
  3. 通过 JNI 调用 com.android.internal.os.ZygoteInit下的 main 函数,初始化整个 Java 层。
  4. 通过 registerZygoteSocket 函数创建服务端 Socket,并通过 runSelectLoop 函数等待 ActivityManagerService 的请求来创建新的应用程序进程。
  5. 启动 SystemServer 进程。

1.3 SystemServer 启动过程

在上一小节中的 ZygoteInit.java 中,调用了 startSystemServer 进程,SystemServer 进程的进程名实际为:system_server,通过前面内容可知,system_server 是zygote 通过 fork 诞生的,在执行 handleSystemServerProcess 方法的时候,发生了什么呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private static void handleSystemServerProcess(
ZygoteConnection.Arguments parsedArgs)
throws ZygoteInit.MethodAndArgsCaller {
closeServerSocket();
...
if (parsedArgs.invokeWith != null) {
...
} else {
ClassLoader cl = null;
if (systemServerClasspath != null) {
cl = createSystemServerClassLoader(systemServerClasspath,
parsedArgs.targetSdkVersion);
Thread.currentThread().setContextClassLoader(cl);
}
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
}
}

结合 copy on write 技术也可以明白,system_server 复制了 zygote 的地址空间,所以也会得到 zygote 所创建的 Socket, 但是对于 system_server 来说,这个 socket 并没有用处,所以用 closeServerSocket 来关闭。

RuntimeInit.zygoteInit() 方法如下:

1
2
3
4
5
6
7
8
9
public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit");
redirectLogStreams();
commonInit();
nativeZygoteInit();
applicationInit(targetSdkVersion, argv, classLoader);
}

通过 native 和 application 两个 init 函数来实现 system_server 的初始化。

另外,在程序中,可以发现 zygote 和 system_server 是同生死的,当 system_server 挂掉,那么 zygote 也会把自己干掉。

1.3.1 启动 Binder 线程池

顺着 nativeZygoteInit() 方法看下去,会发现在

/platform_frameworks_base/cmds/app_process/app_main.cpp

中调用如下的方法:

1
2
3
4
5
6
virtual void onZygoteInit()
{
sp<ProcessState> proc = ProcessState::self();
ALOGV("App process: starting thread pool.\n");
proc->startThreadPool(); // 启动 线程池
}

在注释位置便是启动了一个 Binder 的线程池,用于和其他进程进行通信,可以发现 nativeZygoteInit() 主要便是启动 Binder 线程池。

1.3.2 invokeStaticMain

回到 RuntimeInit.java,可以看到调用了 applicationInit 方法:

1
2
3
4
5
private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
...
invokeStaticMain(args.startClass, args.startArgs, classLoader);
}

其中调用了 invokStaticMian 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
Class<?> cl;
try {
//通过反射返回的 cl (SystemServer类)
cl = Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
"Missing class when invoking static main " + className,
ex);
}
Method m;
try {
m = cl.getMethod("main", new Class[] { String[].class });// main
} catch (NoSuchMethodException ex) {
throw new RuntimeException(
"Missing static main on " + className, ex);
} catch (SecurityException ex) {
throw new RuntimeException(
"Problem getting static main on " + className, ex);
}
int modifiers = m.getModifiers();
if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new RuntimeException(
"Main method is not public and static on " + className);
}
//main函数传入到 MethodAndArgsCaller 异常中并抛出该异常
throw new ZygoteInit.MethodAndArgsCaller(m, argv);
}

截获的 MethodAndArgsCaller 异常在 ZygoteInit.java 的 main 函数中。

1.3.3 SystemSever 庐山真面目

/platform_frameworks_base/services/java/com/android/server/SystemServer.java

zygote 分裂产生 SystemServer ,其实就是调用 com.android.server.SystemServer 的 main 函数, main 函数代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public static void main(String[] args) {
new SystemServer().run();
}
// run 函数
private void run() {
...
System.loadLibrary("android_servers");//加载 libandroid_servers.so
...
// 创建 SystemServiceManager
mSystemServiceManager = new SystemServiceManager(mSystemContext);
LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
...
try {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartServices");
// 启动各式各样的服务
startBootstrapServices();// 引导
startCoreServices(); // 核心
startOtherServices(); // 其他
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
...
}

代码中可以看到把 Service 分为 引导、核心、其他三大类,可以对服务进行管理等操作,常见的服务比如:ActivityManagerService、PowerManagerService、Installer 等,具体启动实现细节不做细节描述,主体为创建 ServiceManager ,通过系统 C/S 架构中的 Binder 进行通信,启动各种系统服务并对其i进行管理。

1.3.4 SystemServer 小结

SystemServer 主要工作为下面三个方面:

  1. 启动 Binder 线程池
  2. 创建SystemServiceManager用于对系统的服务进行创建、启动和生命周期管理
  3. 启动各种系统服务

1.4 Launcher 启动

1.4.1 Launcher 是什么

Android 系统启动的最后一步,是加载 Launcher 应用程序,Launcher 是一个桌面应用,用来展示已经安装的应用程序入口,Launcher 在启动过程中会请 求PackageManagerService 返回系统中已经安装的应用程序的信息,并将这些信息封装成一个快捷图标列表显示在系统屏幕上,这样用户可以通过点击这些快捷图标来启动相应的应用程序。

1.4.2 Launcher 启动流程

在上面的 startOtherService 方法中,有这样几行代码:

1
2
3
4
5
6
mActivityManagerService.systemReady(new Runnable() {
@Override
public void run() {
Slog.i(TAG, "Making services ready");
mSystemServiceManager.startBootPhase(
SystemService.PHASE_ACTIVITY_MANAGER_READY);

可以看到,在 AMS 中会调用 systemReady 方法:

/platform_frameworks_base/services/core/java/com/android/server/am/ActivityManagerService.java

1
2
3
4
5
6
7
8
public void systemReady(final Runnable goingCallback) {
...
synchronized (this) {
...
mStackSupervisor.resumeFocusedStackTopActivityLocked();
mUserController.sendUserSwitchBroadcastsLocked(-1, currentUserId);
}
}

systemReady 方法中调用了 ActivityStackSupervisor 的 resumeFocusedStackTopActivityLocked 方法,其中 ActivityStackSupervisor 是 Activity 堆栈相关,在 resumeFocusedStackTopActivityLocked 会调用 resumeTopActivityInnerLocked 方法,进而调用 ActivityManagerService 的 startHomeActivityLocked 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
boolean startHomeActivityLocked(int userId, String reason) {
// 系统运行模式选择:非工厂模式、低级工厂模式和高级工厂模式
// 这里是低级工厂模式
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
return false;
}
// Intent
Intent intent = getHomeIntent();
ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
if (aInfo != null) {
intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
aInfo = new ActivityInfo(aInfo);
aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
ProcessRecord app = getProcessRecordLocked(aInfo.processName,
aInfo.applicationInfo.uid, true);
//判断符合 Action 为Intent.ACTION_MAIN,
// Category为Intent.CATEGORY_HOME 的应用程序是否已经启动
// 如果没启动则调用注释4的方法启动该应用程序。
Intent.ACTION_MAIN,Category为Intent.CATEGORY_HOME。
if (app == null || app.instrumentationClass == null) {
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
mActivityStarter.startHomeActivityLocked(intent, aInfo, reason);//4
}
} else {
Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
}
return true;
}

上述代码中 Intent 所启动的这个应用程序就是 Launcher,因为 Launcher 的 Manifest 文件中的intent-filter标签匹配了Action为 Intent.ACTION_MAIN,Category 为 Intent.CATEGORY_HOME。

1.4.3 加载应用图标

Launcher 会根据自身的程序设计进行扫描,将已经安装的程序文件的图标显示在桌面,从而实现我们所见到的的 Launcher ,即整个桌面应用。

1.5 系统启动流程总结

通过上述的分析,对整个 Android 的系统启动流程总结如下:

  1. 按下电源键 =》引导程序 Bootloader 加载到 RAM 并开始执行。

  2. 内核启动,加载驱动、设置缓存,完成系统设置等 =》启动 init 进程

  3. init 进程初始化 =》启动 Zygote 进程

  4. 创建 Java VM 、注册 JNI 、创建服务端 Socket 、启动 SystemServer

  5. 启动 Binder 线程池和 SystemServiceManager ,并且启动各种系统服务

  6. SystemServer 进程=》启动 ActivityManagerService =》启动Launcher,Launcher 将已安装应用的快捷图标显示到界面上。

  7. 完成系统启动流程

    对上述过程进行流程图绘制,总结如下:

二、应用启动流程

2.1 Application 基础理论

要想清楚 APP 的启动流程,必须要对其理论知识清晰明确,所以对 Android Application 的基础理论,暂列如下:

  1. Android APP 每个都有自己独立的空间,运行在一个单独的进程,拥有唯一的一个 VM 和 ID。

  2. Android APP 拥有很多不同的组件,相互关联,所以程序没有一个类似程序入口的 main() 方法。

  3. Application 的组件分别为:Activity、Service、Broadcast Receiver、Content Provider。

在 Android 中的进程和 Linux 蕾丝,默认情况下每个 APK 运行在自己的 Linux 进程中,此外,在整个的进程中有一个默认的线程——UI 线程,即主线程,在主线程中可以拿到一个 Looper 实例,通过 Looper.loop() 不断的从 Message 队列中取出 Message 来进行消息传递并作对应的处理。

所以,问题来了,该进程是什么时候启动的呢?

从意图的角度出发,进程在被调用的时候启用,当用户点击图标启动或者通过其他组件来调用 APK 里面的内容时,如果 APK 不属于运行状态,那么系统会为该应用创建一个新的进程来启动,所以,进程,归根到底为在需要的时候才会创建。

回顾一下上面的 Android 系统启动流程, bootloader 启动内核和 init 进程,init进程创建 zygote,在这里补充一点,就是在 init 进程中,会分裂出更多名为 “daemons” 的守护进程,为底层的 Linux 进程来处理底层硬件相关的接口。然后就是 zygote 的一系列操作,继而创建 SystemServer ,然后启动所有的系统核心服务,再这里,就会有一个 Service 被启动,就是 ActivityManagerService ,然后会加载 Launcher ,进入到桌面。

2.2 应用启动过程

在桌面上,用户点击到某一个应用的时候,点击事件会通过 startActivity(Intent) 方法启动一个 Activity,那么点击事件发生后,是如何通知系统创建一个新的 Activity 呢,这里用到的又是 Android 中的进程通信,就是 Binder IPC 机制,最终会调用到 ActivityManagerService ,该 Service 会执行以下几步操作:

  • 通过 PackageManager 的 resolveIntent() 收集这个 intent 的指向信息。
  • 将指向信息存储到一个 intent 中。
  • 通过 grantUriPermissionLocked() 方法验证用户是否有足够的权限调用 intent 指向的目标 Activity
  • 有权限, AMS 会检查并创建一个新的 task 来启动 Activity
  • 检查该进程的 PricessRecord 是否存在,如果为 null AMS 会创建新的进程来实例化 Activity
  • 执行 Activity 的生命周期 =》应用启动完成

启动过程图示:

应用启动过程图示

2.3 进程创建过程

ActivityManagerService 调用 startProcessLocked() 方法来创建新的进程,在该方法中,会通过 socket 将参数传递给 zygote 进程,zygote 分裂,调用 ZygoteInit.man() 方法来实例化 ActivityThread 并最终返回新的进程 pid;随后 ActivityThread 会一次调用 Looper.prepareLoop() 和 Looper.loop() 来开启消息循环。流程图如下(图片来源):

process creation

2.4 绑定 Application

在进程创建完成后,需要将进程和 Application 进行绑定,通过调用 ActivityThread 对象中的 bindApplication() 方法来完成,发送一个 BIND_APPLICATION 消息到 MessageQueue 中,进而通过 handlerBindApplication() 方法处理该消息,调用 makeApplication() 方法来加载 App 的 classes 到内存中。

流程图如下:

bind application

2.5 启动 Activity

经过上述步骤,系统中便存在了该 Application 的进程,后面的调用顺序为从一个已经存在的进程中启动一个新进程的 Activity了。

调用方法为 realStartActivity() 它会调用 application 对象中的 sheduleLaunchActivity 发送一个 LAUNCH_AVTIVITY 的消息到消息队列中,通过 handleLaunchActivity() 来处理该消息。

至此,应用启动流程结束,进入到应用的生命周期。

参考资料

坚持原创技术分享,您的支持将鼓励我继续创作!