一、写在前面
在开始之前,你需要知道下面几点:
- 有一份编译好的 Android 源码,现在的 AS 基本能满足,动手跟着步骤走,理解更深刻
- 对 Binder 机制有一定的了解
- 本文基于 API 26,用什么版本的源码并不重要,大体的流程并无本质上的区别
- 从用户手指触摸点击桌面图标到 Activity 启动
关键类简介
- ActivityManagerService:AMS 是 Android 中最核心的服务之一,主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,其职责与操作系统中的进程管理和调度模块相类似,它本身也是一个 Binder 的实现类,应用进程能通过 Binder 机制调用系统服务。
- ActivityThread:应用的入口类,系统通过调用main函数,开启消息循环队列。ActivityThread 所在线程被称为应用的主线程(UI 线程)。
- Instrumentation:工具类,它用来监控应用程序和系统的交互,包装了 ActivityManagerService 的调用,一些插件化方案就是通过 hook 该类实现的。
- ActivityStarter:Activity 启动的工具类,处理启动 Activity 的各种 flag 。
- ActivityStackSupervisor:管理所有应用的 Activity 的栈,其中 mFocusedStack 就是当前应用的 Activity 栈。
应用进程介绍
- 在大多数情况下,每个 Android 应用都在各自的 Linux 进程中运行。当需要运行应用的一些代码时,系统会为应用创建此进程,并使其保持运行,直到不再需要它且系统需要回收其内存以供其他应用使用。
- 应用进程的生命周期并不由应用本身直接控制,而是由系统综合多种因素来确定的,比如系统所知道的正在运行的应用部分、这些内容对用户的重要程度,以及系统中可用的总内存量。这是 Android 非常独特的一个基本功能。
- 当应用组件启动且该应用未运行任何其他组件时,Android 系统会使用单个执行线程为应用启动新的 Linux 进程。默认情况下,同一应用的所有组件会在相同的进程和线程(称为“主”线程)中运行。如果某个应用组件启动且该应用已存在进程(因为存在该应用的其他组件),则该组件会在此进程内启动并使用相同的执行线程。但是,您可以安排应用中的其他组件在单独的进程中运行,并为任何进程创建额外的线程。
- 每个应用进程都相当于一个 Sandbox 沙箱,Android 通过对每一个应用分配一个 UID,注意这里的 UID 不同于 Linux 系统的 User ID,可以将每个应用理解为一个 User ,只能对其目录下的内容具有访问和读写权限。
- Android 利用远程过程调用 (RPC) 提供了一种进程间通信 (IPC) 机制,在此机制中,系统会(在其他进程中)远程执行由 Activity 或其他应用组件调用的方法,并将所有结果返回给调用方。因此,您需将方法调用及其数据分解至操作系统可识别的程度,并将其从本地进程和地址空间传输至远程进程和地址空间,然后在远程进程中重新组装并执行该调用。然后,返回值将沿相反方向传输回来。Android 提供执行这些 IPC 事务所需的全部代码,因此您只需集中精力定义和实现 RPC 编程接口。
下面这张图可以补充理解一下进程的概念:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L0wkWHQT-1632919531109)(//upload-images.jianshu.io/upload_images/1856419-c04a4f0fb34a7926.png?imageMogr2/auto-orient/strip|imageView2/2/w/678/format/webp)]
二、流程分析
先来一张流程简图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dv73EZ0Y-1632919531112)(//upload-images.jianshu.io/upload_images/1856419-234677e55f408b9c.png?imageMogr2/auto-orient/strip|imageView2/2/w/1106/format/webp)]
下面是流程详细图,带你看完整个启动流程及其所涉及到的类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4geW8WPo-1632919531115)(//upload-images.jianshu.io/upload_images/1856419-f334b3f2122b430c.png?imageMogr2/auto-orient/strip|imageView2/2/w/917/format/webp)]
下面补充一张 Gityuan 大神的系统启动架构图帮助理解,其实只要看看这张图的上半部分就足够了:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3MMQZcf5-1632919531121)(//upload-images.jianshu.io/upload_images/1856419-88d738be242f6233.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1200/format/webp)]
三、概述
简单地讲,从 用户手指触摸点击桌面图标到 Activity启动 可以用下面 4 步概括:
- 当点击桌面 App 的时候,发起进程就是 Launcher 所在的进程,启动远程进程,利用 Binder 发送消息给 system_server 进程;
- 在 system_server 中,启动进程的操作会先调用
ActivityManagerService#startProcessLocked()
方法,该方法内部调用Process.start(android.app.ActivityThread)
;而后通过 socket 通信告知 Zygote 进程 fork 子进程,即 app 进程。进程创建后将 ActivityThread 加载进去,执行ActivityThread#main()
方法;
- 在 app 进程中,
main()
方法会实例化 ActivityThread,同时创建 ApplicationThread,Looper,Handler 对象,调用ActivityThread#attach(false)
方法进行 Binder 通信,方法里面调用ActivityManagerService#attachApplication(mAppThread)
方法,将 thread 信息告知 ActivityManagerService , 接着 Looper 启动循环;
- 回到 system_server 中,
ActivityManagerService#attachApplication(mAppThread)
方法内部调用了thread#bindApplication()
和mStackSupervisor#attachApplicationLocked()
我们依次讲解这两个方法;
4.1thread#bindApplication()
方法调用了ActivityThread#sendMessage(H.BIND_APPLICATION, data)
方法,最终走到了ActivityThread#handleBindApplication()
,进而创建 Application 对象,然后调用Application#attach(context)
来绑定 Context ,创建完 Application 对象后便是调用mInstrumentation#callApplicationOnCreate()
执行Application#onCreate()
生命周期;
4.2mStackSupervisor#attachApplicationLocked()
方法中调用app#thread#scheduleLaunchActivity()
即ActivityThread#ApplicationThread#scheduleLaunchActivity()
方法,进而通过ActivityThread#sendMessage(H.LAUNCH_ACTIVITY, r)
方法,最终走到了ActivityThread#handleLaunchActivity()
,进而创建 Activity 对象,然后调用activity.attach()
方法,再调用mInstrumentation#callActivityOnCreate()
执行Activity#onCreate()
生命周期;
四、源码调用探究
对应本文第一张流程图的每一个步骤,下面逐步来看看源码是怎么调用的:
STEP 1
用户点击 app 图标;
STEP 2
Launcher 捕获点击事件,调用 Activity#startActivity()
;
STEP 3
Activity#startActivity()
调用 Instrumentation;
Activity.java
@Override
public void startActivity(Intent intent) {
this.startActivity(intent, null);
}
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
// Note we want to go through this call for compatibility with
// applications that may have overridden the method.
startActivityForResult(intent, -1);
}
}
public void startActi