Android 应用启动流程分析,10 天拿到字节跳动 Android 岗位 offer
一般来说,冷启动包括了以下内容:
启动进程 点击图标发生在 Launcher 应用的进程,startActivity()函数最终是由 Instrumentation 通过 Android 的 Binder 跨进程通信机制 发送消息给 system_server 进程; 在 system_server 中,启动进程的操作由 ActivityManagerService 通过 socket 通信告知 Zygote 进程 fork 子进程(app 进程)
开启主线程 app 进程启动后,首先是实例化 ActivityThread,并执行其 main()函数:创建 ApplicationThread,Looper,Handler 对象,并开启主线程消息循环
Looper.loop()
。创建并初始化 Application 和 Activity ActivityThread 的 main()调用
ActivityThread#attach(false)
方法进行 Binder 通信,通知 system_server 进程执行ActivityManagerService#attachApplication(mAppThread)
方法,用于初始化 Application 和 Activity。 在 system_server 进程中,ActivityManagerService#attachApplication(mAppThread)
里依次初始化了 Application 和 Activity,分别有 2 个关键函数: -thread#bindApplication()
方法通知主线程 Handler 创建 Application 对象、绑定 Context 、执行 Application#onCreate() 生命周期 -mStackSupervisor#attachApplicationLocked()
方法中调用ActivityThread#ApplicationThread#scheduleLaunchActivity()
方法,进而通过主线程 Handler 消息通知创建 Activity 对象,然后再调用mInstrumentation#callActivityOnCreate()
执行 Activity#onCreate() 生命周期布局 &绘制 源码流程可以参考[Android View 的绘制流程分析及其源码调用追踪](
)
至此,应用启动流程完成。
其中 1、2、3 的源码流程可以参考[Android Application 启动流程分析及其源码调用探究](
),但代码细节不是本篇重点。
下面说说上述流程中的几个关键角色,以及其作用:
3.1 zygote 进程
这里稍微说下 Android 系统下的进程机制,每个应用运行时都是:
一个单独的 dalvik 虚拟机(DVM) java 代码在编译后需要运行在 JVM 上,同样 android 中使用了 java 语言,也需要运行在一个 VM 上。所以谷歌针对手机处理器和内存等硬件资源不足而研究出来 DVM,为 android 运行提供环境。 参考[JVM 与 DVM 的关系](
)2. 一个单独的进程 每个应用启动都运行一个单独的 DVM,每个 DVM 单独占用一个 Linux 进程。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。 dalvik 进程管理是依赖于 linux 的进程体系结构的,如要为应用程序创建一个进程,它会使用 linux 的 fork 机制来复制一个进程。
众所周知,Android 是基于 Linux 系统的,在 Linux 中所有的进程都是由 init 进程直接或者是间接 fork 出来的。fork 进程往往比创建进程效率更高。在 Android 中,所有的应用的进程都是由 zygote 进程 fork 出来的。
提到 zygote 进程,就不得不介绍下 Android 开机流程:
Android 手机开机 Linux 内核启动后,会加载 system/core/init/init.rc 文件,启动 init 进程。这个进程是 Android 系统特有的初始化程序,简单介绍下它的工作:
各种复杂工作
负责开关机画面
文件系统的创建和挂载
启动 Zygote(孵化器)进程
启动 ServiceManager,它是 Binder 服务管理器,管理所有 Android 系统服务
在系统启动后 init 进程会 fork Zygote 进程,Zygote 作为孵化器进程,它的 main 函数会创建好自己的环境准备孵化子进程,并开始等待孵化请求:
创建一个 server 端的 socket, name 为 zynote,用于和客户端进程通信
预加载类和资源,提高应用启动速度
启动 SystemServer 进程
监听 socket,当有一个应用程序启动时,就会向它发出请求,然后 zygote 进程 fock 自己来创建的一个新的子进程。
Zygote 进程首先会 fork 自己孵化出 SystemServer 进程,它的 main 函数主要负责:
启动 binder 线程池,这是 SystemServer 与其他进程通信的基础
初始化 Looper
创建了 SystemServiceManager 对象,它会启动 Android 中的各种服务。包括 AMS、PMS、WMS
启动桌面进程,这样才能让用户见到手机的界面。
开启 loop 循环,开启消息循环,SystemServer 进程一直运行,保障其他应用程序的正常运行。
当系统里面的 zygote 进程运行之后,后续启动 APP,就相当于开启一个新的进程。而 Android 为了实现资源共用和更快的启动速度,子进程都是通过 zygote 进程 fork 出来的。所以说,除了 init 进程 fork 出来的第一个进程 zygote,其他应用进程都是 zygote 的子进程,也不难理解为何这个孵化器进程的英文名叫 zygote(受精卵),因为所有的应用进程都是由它孵化诞生。
3.2 SystemServer 进程
SystemServer 是由 zygote 进程 fork 出来的第一个进程,SystemServer 和 Zygote 是 Android Framework 最重要的 2 个进程。 系统里面重要的服务都是在这个进程里面开启的,比如 ActivityManagerService、PackageManagerService、WindowManagerService。
应用启动流程基本是围绕着 ActivityManagerService 和 ActivityThread 展开。
3.3 Android 系统里的 Client/Server 模式
平时我们所熟知的前端(Web\Android\iOS)通过网络与服务器通信是客户端-服务端模式的体现,而在 Android Framework 中,四大组件的创建、生命周期也是通过这样的模式进行通信:
服务器端(server)指的就是 SystemServer 进程,这个进程提供了很多服务 比如 AMS、PMS、WMS 等等,所有的 App 进程都可以与其通信。
客户端(client)指的就是各个独立的 App 进程。
Android 开发者都应该知道,通过包名和 Activity 类名就可以打开一个 APP。实际上,项目里的业务代码 startActivity()方法并不是直接创建进程、拉起 APP 的。而是通过一系列的调用,把请求传递给 SystemServer 的 AMS。AMS 收到来自客户端的请求后,再通知 zygote 进程来 fork 一个新进程,来开启我们的目标 App 的。 这就像是在浏览器里打开一个网页,浏览器把 url 和参数发送到服务器,然后还是服务器处理请求,并返回相应的 html 并展示在浏览器上。
这个过程涉及到 3 个进程:App 进程、AMS(SystemServer 进程)、zygote 进程。
App 进程与 AMS 通过 Binder 机制进行跨进程通信
AMS(SystemServer 进程)与 zygote 通过 Socket 进行跨进程通信。
在 Android 系统中,任何一个 Activity 的启动都是由 AMS 和 App 进程(主要是 ActivityThread)相互配合来完成的。AMS 服务统一调度系统中所有进程的 Activity 启动,而每个 Activity 的启动过程则由其所属的进程具体来完成。
3.4 Android Binder 机制
我们知道 AMS 与 ActivityThread 的交互主要是通过进程间通信 (IPC) 。跨进程通信的机制就是将方法调用及其数据分解至操作系统可识别的程度,并将其从本地进程和地址空间传输至远程进程和地址空间,然后在远程进程中重新组装并执行该调用。 Android 提供了执行这些 IPC 事务的方案——Binder 机制,因此我们只需关心接口定义和实现 RPC 编程接口。
而 App 进程与 SystemServer 进程也是通过 Binder 机制进行进程间通信,Android 为此设计了两个 Binder 接口:
IApplicationThread: 作为系统进程请求应用进程的接口;
IActivityManager: 作为应用进程请求系统进程的接口。
对于一个 Binder 接口,在客户端和服务端各有一个实现:Proxy 和 Native,它们之间的通信主要是通过 transact 和 onTransact 触发。一般从命名上可以区分:xxxNative 是在本进程内的 Binder 代理类,xxxProxy 是在对方进程的 Binder 代理类。
多说一句,这些 Binder 都由 ServiceManager 统一管理:
ServiceManager 管理所有的 Android 系统服务,有人把 ServiceManager 比喻成 Binder 机制中的 DNS 服务器,client 端应用如果要使用系统服务,调用 getSystemService 接口,ServiceManager 就会通过字符串形式的 Binder 名称找到并返回对应的服务的 Binder 对象。
它是一个系统服务进程,在 native 层启动,它在 system/core/rootdir/init.rc 脚本中描述并由 init 进程启动。
ServiceManager 启动后,会通过循环等待来处理 Client 进程的通信请求。
App 进程与 SystemServer 进程的 Binder 接口如下图:
3.4.1 服务端 IActivityManager——ActivityManagerNative——ActivityManagerService
ActivityManagerNative 作为服务端的“桩(Stub)”,其主要职责就是对远程传递过来的数据进行”反序列化(unparcel)”;
ActivityManagerProxy 作为服务的“代理(Proxy)”,运行在客户端,其主要职责就是将数据进行“序列化(parcel)”,再传递给远程的“桩(Stub)”,App 使用 AMS 提供的功能,比如 startActivity,是通过 AMS 在客户端的代理 ActivityManagerProxy 发起的。
最下面一层是桩(Stub)的具体实现——AMS(ActivityManagerService),负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,非常复杂。
AMS 是一个系统服务,在 SystemServer 进程启动后完成初始化。在应用启动流程中,充当着服务端的角色。 App 中 Activity 的生命周期由 AMS 管理,它决定着什么时候该调用 onCreate、onResume 这些生命周期函数,把 Activity 放在哪个栈里,上下文之间的关系是怎样的等等。
比如:
startActivity 最终调用了 AMS 的 startActivity 系列方法,实现了 Activity 的启动;Activity 的生命周期回调,也在 AMS 中完成;
startService,bindService 最终调用到 AMS 的 startService 和 bindService 方法;
动态广播的注册和接收在 AMS 中完成(静态广播在 PMS 中完成)
getContentResolver 最终从 AMS 的 getContentProvider 获取到 ContentProvider
3.4.2 客户端 IApplicationThread——ApplicationThreadNative——ActivityThread
桩(Stub):ApplicationThreadNative
代理(Proxy):ApplicationThreadProxy,App 在客户端进程中实现了实例化 Activity、调用 onCreate 等生命周期函数的功能,因为跨进程也不能被 AMS 直接调用,而是 AMS 通过客户端的代理 ApplicationThreadProxy 来处理。
最下面一层是桩(Stub)的具体实现——ApplicationThread,它是 ActivityThread 的一个内部类,ApplicationThread 负责响应系统进程发起的请求,而实际触发的业务逻辑是在 ActivityThread 中。与一般的代理模式不同,它不是直接持有 ActivityThead 的一个引用,而是把处理的请求发到 ActivityThread 内部的一个 Handler 上。
和服务端的 AMS 相对应,ActivityThread 在应用启动的 Client/Server 模式中,是作为客户端那一边的具体实现。它并不是一个线程,但它包含了一个应用进程的主线程运作的全部机制:
启动应用的主线程,并开启消息循环
提供了一个 IActivityThread 接口作为与 AMS 的通讯接口,通过该接口 AMS 可以将 Activity 的状态变化传递到客户端的 Activity 对象
3.5 启动一个 Activity 的通信过程
我们已经知道应用进程创建以后,App 进程的 ActivityThread 与 SystemServer 进程的 AMS 通过 Binder 进行通信。 在文章前面【2 应用启动流程】提到,在 ActivityThread 的 main 方法中,调用 ActivityThread#attach(false)
方法进行 Binder 通信,通知 system_server 进程执行 ActivityManagerService#attachApplication(mAppThread)
方法,用于初始化 Application 和 Activity。
可以结合源码流程,稍微总结一下这个通信过程。
3.5.1 Application 的初始化
从应用进程到系统进程
在 ActivityThread 创建的时候,会将自己的 ApplicationThread 绑定到 AMS 中:
ActivityThread.main()└── ActivityThread.attach()└── IActivityManager.attachApplication(mAppThread)└── Binder.transact()
应用进程作为客户端,通过 IAcitivtyManager 接口发起了跨进程调用,跨进程传递的参数 mAppThread 就是 IApplicationThread 的实例,执行流程从应用进程进入到系统进程:
ActivityManagerService.onTransact()└── ActivityManagerService.attachApplication(IApplicationThread thread)
AMS 作为 IActivityManager 接口的服务端实现,会响应客户端的请求,最终 AMS.attachApplication()函数会被执行,该函数接收跨进程传递过来的 IApplicationThread 实例,将存在系统进程维护的 ProcessRecord 中。 具体细节可以看 AMS 的源码,我们只需要知道 AMS 中维护了所有进程运行时的信息(ProcessRecord),一旦发生了应用进程的绑定请求,ProcessRecord.thread 就被赋值成应用进程的 IApplicationThread 实例,这样一来,在 AMS 中就能通过该 IApplicationThread 实例发起向应用进程的调用。
从系统进程到应用进程
在 AMS.attachApplication()的过程中,会有一些信息要传递给应用进程,以便应用进程的初始化,系统进程会发起如下函数调用:
ActivityManagerService.attachApplication()└── ActivityManagerService.attachApplicationLocked()└── IApplicationThread.bindApplication(processName, appInf
o ...)└── Binder.transact()
此时,AMS 会反转角色,即系统进程作为客户端,通过 IApplicationThread 接口向应用进程发起调用。
AMS 通过 ProcessRecord 来维护进程运行时的状态信息,需要将应用进程绑定到 ProcessRecord 才能开始一个 Application 的构建;
AMS 维护的 ProcessRecord 这个数据结构,包含了进程运行时的信息,譬如应用进程的名称 processName、解析 AndroidManifest.xml 得到的数据结构 ApplicationInfo 等,其中,要传递给应用进程的数据都是 Parcelable 类型的实例。
应用进程响应请求的调用关系如下所示:
ApplicationThread.onTransact()└── ApplicationThread.bindApplication()└── ActivityThread.H.handleMessage(BIND_APPLICATION)└── ActivityThread.handleBindApplication()└── Application.onCreate()
ApplicationThread 作为 IApplicationThread 接口的服务端实现,运行在应用进程中,然后 ApplicationThread.bindApplication()会被执行,完成一些简单的数据封装(AppBindData)后,通过 Handler 抛出 BIND_APPLICATION 消息。这一抛,就抛到了主线程上,ActivityThread.handleBindApplication()会被执行,终于创建了 Application 对象,然后调用 Application#attach(context) 来绑定 Context,并调用 Application.onCreate()函数。历经应用进程和系统进程之间的一个来回,总算是创建了一个应用程序。
Android 源码里有较统一的函数命名方式,在 AMS 中与 Activity 管理相关很多函数都会带有 Locked 后缀,表示这些函数的调用都需要多线程同步操作(synchronized),它们会读/写一些多线程共享的数据
3.5.2 Activity 的初始化
前面提到在 system_server 进程中,ActivityManagerService#attachApplication(mAppThread)
里依次初始化了 Application 和 Activity,其中的mStackSupervisor#attachApplicationLocked(ProcessRecord)
里进行了 Activity 的初始化。
AMS 通过 ActivityRecord 来维护 Activity 运行时的状态信息,需要将 Activity 绑定到 AMS 中的 ActivityRecord 能开始 Activity 的生命周期。
在 Activity 类中有一个 IBinder 类型的属性:
private IBinder mToken;
,IBinder 类型表示这个属性是一个远程对象的引用,Token 持有了一个 ActivityRecord 实例的弱引用。在创建一个 ActivityRecord 的时候,就会创建了一个 Token 类型的对象。
在启动一个新的 Activity 时,AMS 会将 ActivityRecord 的 Token 传递给应用进程,调用关系如下所示:
评论