原文链接:blog.csdn.net/u010257931/…
Android App 启动后续流程
在深入细节之前,我们必须先理解整个流程背后的两个核心设计原则:
- 进程隔离与通信: App 进程和
system_server进程是完全独立的。它们之间唯一的沟通方式是 Binder IPC (Inter-Process Communication)。这就像两个不同国家的人打电话,必须通过一个共有的电话网络(Binder 驱动)和遵循特定协议(AIDL 接口)。 - UI 线程与线程安全: 在 App 进程内部,所有 UI 操作和生命周期回调都必须在主线程(UI 线程)执行。因此,从
system_server通过 Binder 传来的请求,通常先由一个 Binder 线程接收,然后必须通过Handler机制切换到主线程去执行。
现在,让我们带着这两个原则,一步步走完整个旅程。
流程图 (增强版)
sequenceDiagram
participant Zygote
participant App Process (Main Thread & Binder Threads)
participant System Server (ATMS)
Zygote ->> App Process (Main Thread & Binder Threads): fork() a new process
App Process (Main Thread & Binder Threads)->>App Process (Main Thread & Binder Threads): **ActivityThread.main()**<br/>- Looper.prepareMainLooper()<br/>- new ActivityThread()
App Process (Main Thread & Binder Threads)->>System Server (ATMS): **[Binder IPC]** 1. attachApplication(appThreadBinder)<br/>*App Process sends its Binder proxy to ATMS.*
System Server (ATMS)->>System Server (ATMS): **attachApplicationLocked()**<br/>*ATMS receives the proxy and prepares app data.*
System Server (ATMS)->>App Process (Main Thread & Binder Threads): **[Binder IPC]** 2. thread.bindApplication(appData)<br/>*ATMS calls back to the App Process.*
App Process (Main Thread & Binder Threads)->>App Process (Main Thread & Binder Threads): **[Thread Switch]** 3. Binder thread receives call,<br/>sends H.BIND_APPLICATION message to Main thread.
App Process (Main Thread & Binder Threads)->>App Process (Main Thread & Binder Threads): **[On Main Thread]** 4. handleBindApplication()<br/>- Creates Application via Instrumentation.<br/>- Calls **Application.onCreate()**.
Note right of App Process (Main Thread & Binder Threads): --- Application Ready ---
System Server (ATMS)->>System Server (ATMS): 6. mAtmInternal.attachApplication()<br/>*Now, prepare to launch the Activity.*
System Server (ATMS)->>System Server (ATMS): 7. realStartActivityLocked()<br/>*Builds a ClientTransaction with LaunchActivityItem.*
System Server (ATMS)->>App Process (Main Thread & Binder Threads): **[Binder IPC]** 8. scheduleTransaction(transaction)<br/>*ATMS sends the "to-do list" to the App Process.*
App Process (Main Thread & Binder Threads)->>App Process (Main Thread & Binder Threads): **[Thread Switch]** 9. Binder thread receives call,<br/>sends H.EXECUTE_TRANSACTION message to Main thread.
App Process (Main Thread & Binder Threads)->>App Process (Main Thread & Binder Threads): **[On Main Thread]** 10. TransactionExecutor.execute()<br/>- Executes LaunchActivityItem callback.
App Process (Main Thread & Binder Threads)->>App Process (Main Thread & Binder Threads): **[On Main Thread]** 11. handleLaunchActivity()<br/>- Creates Activity via Instrumentation.<br/>- Calls activity.attach().<br/>- Calls **Activity.onCreate()**.
Note right of App Process (Main Thread & Binder Threads): --- Activity Ready & Visible ---
阶段一:App 进程初始化与“向系统报到”
Step 1.1: 入口 - ActivityThread.main()
- 发生了什么? Zygote
fork成功后,新的 App 进程就诞生了。它的第一行 Java 代码就是ActivityThread.main()。 - 关键代码:
Looper.prepareMainLooper(): 这是 App 的心跳之源。它为当前线程(即主线程)创建一个Looper和一个MessageQueue,让它具备了处理消息循环的能力。没有这一步,App 就会执行完main方法后直接退出。ActivityThread thread = new ActivityThread(): 创建ActivityThread实例。这个类是 App 进程中实际的“主宰”,它管理着进程中所有Activity、Service等组件的生命周期。thread.attach(false, startSeq): 这是启动流程的第一个关键转折点。
Step 1.2: 跨进程通信 - thread.attach()
- 发生了什么?
attach方法的核心是通过 Binder IPC 向system_server进程中的 ATMS 发起一个远程调用:mgr.attachApplication(mAppThread, ...)。 - 目的 (Why)? 这是 App 进程在向系统“报到”和“注册”。它在说:“你好 ATMS,我这个 PID 的进程已经由 Zygote 创建好了。这是我的通信地址(
mAppThread),以后有任何关于我的事,请通过这个地址联系我。” mAppThread是什么? 它是ActivityThread的一个内部类ApplicationThread的实例。ApplicationThread继承了IApplicationThread.Stub,这意味着它是一个 Binder 服务端对象。当mAppThread被传递给 ATMS 后,ATMS 得到的是它的 Binder 代理对象。这样,ATMS 就可以像调用本地方法一样调用 App 进程里的方法了。
Key Player Spotlight:
ActivityThreadvs.ApplicationThread
ActivityThread: 是 App 进程的“大管家”,负责管理一切。它发起对system_server的调用。ApplicationThread: 是ActivityThread的“信使”或“秘书”,它是一个 Binder 对象,专门负责接收来自system_server的指令。职责非常清晰。
阶段二:Application 的创建 (系统回调)
Step 2.1: 系统响应 - ATMS.attachApplicationLocked()
- 发生了什么? ATMS 在自己的进程中接收到了来自 App 进程的
attachApplication调用。它会根据pid找到之前为这个 App 准备好的所有信息(ApplicationInfo、权限、配置等)。 - 关键动作: 准备工作完成后,ATMS 就要开始回调 App 进程了。它调用
thread.bindApplication(...)。这里的thread就是上一步 App 进程传过来的 Binder 代理对象。
Step 2.2: 跨进程回调 - thread.bindApplication()
- 发生了什么? ATMS 通过 Binder 调用 App 进程的
ApplicationThread的bindApplication方法,并把所有应用信息作为参数传过去。 - 目的 (Why)? 这是 ATMS 在下达第一个正式指令:“报到已收到。现在,根据我给你的这些信息,把你的
Application对象创建出来。”
Step 2.3: 线程切换 - The Handler Switch
- 发生了什么?
bindApplication方法是在 App 进程的一个 Binder 线程中执行的(Binder 调用总是在独立的线程池中处理)。但创建Application涉及到后续的 UI 操作,必须在主线程完成。 - 关键动作:
ApplicationThread立即将所有接收到的参数打包成一个AppBindData对象,然后调用sendMessage(H.BIND_APPLICATION, data)。H是ActivityThread内部的一个Handler,它与主线程的Looper绑定。这样,消息就被发送到了主线程的消息队列中。
Step 2.4: 主线程执行 - handleBindApplication()
- 发生了什么? 主线程的
Looper从消息队列中取出BIND_APPLICATION消息,并调用ActivityThread的handleBindApplication方法来处理。 - 关键动作:
data.info.makeApplicationInner(...): 这是创建Application的核心。data.info是一个LoadedApk对象,它代表了已加载的 APK 信息。- 内部通过
mInstrumentation.newApplication(...)来实例化Application。 app.attach(context): 将创建好的Application实例与一个ContextImpl绑定,使其具备完整的上下文能力。mInstrumentation.callApplicationOnCreate(app): 这个调用最终会触发我们所熟知的Application.onCreate()。
Key Player Spotlight:
InstrumentationInstrumentation类是 Android 的一个“钩子”机制。系统并不直接new Application()或new Activity(),而是通过Instrumentation对象来创建。这使得测试框架(如 Espresso)可以替换掉默认的Instrumentation,从而在测试时注入模拟(Mock)的Application或Activity,实现对应用的完全控制。
阶段三:首个 Activity 的创建 (事务驱动)
Step 3.1 & 3.2: 系统准备启动 Activity - realStartActivityLocked()
- 发生了什么? 回到
system_server进程。在attachApplicationLocked方法的后半部分,系统确认 App 进程已经准备好后,就开始启动入口Activity。经过mAtmInternal.attachApplication()-> ... 等一系列调用,最终到达ActivityStackSupervisor.realStartActivityLocked()。 - 关键动作: Android 现代版本引入了
ClientTransaction(客户端事务) 的概念来管理生命周期。系统不再是零散地发送pause、resume等命令,而是把一系列操作打包成一个“事务”。ClientTransaction.obtain(...): 创建一个事务。clientTransaction.addCallback(LaunchActivityItem.obtain(...)): 向事务中添加一个LaunchActivityItem回调项。这个回调项包含了启动新Activity所需的所有信息(Intent、ActivityInfo 等)。clientTransaction.setLifecycleStateRequest(ResumeActivityItem.obtain()): 设置这个事务执行完毕后,Activity应该达到的最终状态(Resumed)。
Step 3.3 - 3.5: 发送并执行事务
- 发生了什么?
mService.getLifecycleManager().scheduleTransaction(clientTransaction): ATMS 通过 Binder 将这个打包好的“事务”发送给 App 进程。- 又一次线程切换: App 进程的
ApplicationThread在 Binder 线程接收到scheduleTransaction调用,然后再次通过Handler发送EXECUTE_TRANSACTION消息给主线程。 - 主线程执行:
ActivityThread的handleMessage收到消息后,调用mTransactionExecutor.execute(transaction)。
Step 3.6 - 3.8: Activity 的诞生
- 发生了什么?
TransactionExecutor开始按顺序执行事务中的项目。 - 关键动作:
executeCallbacks(): 执行事务中的回调列表。我们的LaunchActivityItem就在其中。LaunchActivityItem.execute(): 这个回调被执行,它调用client.handleLaunchActivity()(client就是ActivityThread)。handleLaunchActivity()->performLaunchActivity(): 这是Activity诞生的最终舞台。mInstrumentation.newActivity(...): 再次通过Instrumentation使用反射创建Activity实例。activity.attach(...): 这是非常关键的一步!它将一个ContextImpl、一个PhoneWindow(窗口的具体实现)等核心组件与Activity实例关联起来。此时,Activity才真正“活了过来”。mInstrumentation.callActivityOnCreate(...): 这个调用链最终会执行activity.performCreate(),在其中,我们重写的Activity.onCreate(Bundle)被正式调用。
至此,从一个虚无的进程,到 Application 上下文的建立,再到第一个 Activity 界面的 onCreate 被调用,整个复杂的启动流程才算完成。接下来的 onStart, onResume 以及界面的测量、布局、绘制将由 WindowManager 和 View 系统接管。
好的,遵照你的要求,我在上次的基础上,结合两篇文章的完整流程,为你补充了更多、更深入的面试题和答案。
这些问题不仅覆盖了“是什么”和“怎么做”,更侧重于考察“为什么这么设计”,这在高级工程师的面试中尤为重要。
补充面试题与详解 (高级/资深)
问题 5:Zygote fork 出新进程后,App 进程是如何“主动”与 SystemServer 建立联系的?这个“握手”过程的关键是什么?
答: Zygote
fork出的新进程并不是被动等待SystemServer的指令,而是会主动反向注册到SystemServer,这个过程可以理解为一次“握手”或“报到”。
- 入口与初始化:新进程的入口是
ActivityThread.main()方法。在该方法中,会创建主线程的Looper,并实例化ActivityThread类。- 关键的
attach()调用:ActivityThread实例创建后,会立即调用其attach()方法。- 发起 Binder IPC:
attach()方法的核心是,通过 Binder IPC 调用ActivityTaskManagerService的attachApplication()方法。- 传递通信信物:在这次调用中,最关键的参数是
mAppThread。mAppThread是ActivityThread的一个内部类ApplicationThread的实例,它是一个 Binder 对象。当它被传递给SystemServer后,SystemServer就持有了这个 App 进程的 Binder 代理。这个“握手”过程的关键在于:App 进程通过
attachApplication()调用,将自己的通信能力(ApplicationThread这个 Binder 服务端)注册给了SystemServer。从此以后,SystemServer就可以通过这个 Binder 代理,反向调用 App 进程中的方法,从而下达创建Application和Activity的指令。没有这次主动的“握手”,SystemServer就无法与新创建的 App 进程建立通信。
问题 6:在 App 进程中,Application.onCreate() 和 Activity.onCreate() 的调用是由两次不同的 Binder 通信触发的,请分别描述这两次通信的发起者、接收者和核心目的。
答: 是的,
Application和Activity的创建流程是分离的,由两次独立的、从SystemServer到 App 进程的 Binder 通信驱动。第一次通信:创建 Application
- 发起者:
SystemServer进程中的ActivityTaskManagerService(ATMS)。- 触发时机:在 ATMS 的
attachApplicationLocked()方法中,当它收到了 App 进程的“握手”请求后。- 调用的方法:通过 App 进程的 Binder 代理,调用
IApplicationThread.bindApplication()。- 接收者:App 进程中的
ApplicationThread(运行在 Binder 线程)。- 核心目的:指令 App 进程加载应用数据,创建
ContextImpl和Application对象,并最终回调Application.onCreate()。完成这次通信后,App 的“应用”层面就绪了。第二次通信:创建 Activity
- 发起者:
SystemServer进程中的ActivityTaskManagerService(ATMS)。- 触发时机:在
Application的绑定流程之后,ATMS 确认 App 进程已准备好,于是在ActivityStackSupervisor.realStartActivityLocked()等方法中准备启动Activity。- 调用的方法:通过
ClientLifecycleManager调用IApplicationThread.scheduleTransaction()。- 接收者:App 进程中的
ApplicationThread(运行在 Binder 线程)。- 核心目的:指令 App 进程执行一个“客户端事务”(
ClientTransaction),这个事务中包含了LaunchActivityItem回调。App 进程收到后,会去执行这个回调,从而完成Activity的创建、attach窗口,并最终回调Activity.onCreate()。完成这次通信后,App 的“界面”层面才开始就绪。总结:
bindApplication是为了建立应用的全局状态和上下文,而scheduleTransaction则是为了具体地创建和展示 UI 组件。
问题 7:在 App 进程中,从接收到 Binder 调用到执行 onCreate,都涉及到了 Handler 的线程切换,为什么必须进行这次切换?请描述其工作机制。
答: 这次
Handler线程切换是绝对必要的,因为 Android 的 UI 操作和组件生命周期回调都必须在**主线程(UI 线程)**执行。而来自SystemServer的 Binder 调用是在 App 进程的 Binder 线程池中被处理的,如果在 Binder 线程中直接创建Application或Activity并操作 UI,会引发严重的线程安全问题和程序崩溃。工作机制如下:
- Binder 线程接收:
SystemServer调用bindApplication或scheduleTransaction后,App 进程中一个空闲的 Binder 线程会接收到这个请求并开始执行ApplicationThread中的对应方法。- 打包并发送消息:在
ApplicationThread的方法中,它并不会执行实际的业务逻辑。而是将所有参数数据打包(例如打包成AppBindData对象或直接传递ClientTransaction对象),然后调用ActivityThread中的Handler(mH)发送一个消息到主线程的消息队列。
- 对于
bindApplication,发送的是H.BIND_APPLICATION消息。- 对于
scheduleTransaction,发送的是H.EXECUTE_TRANSACTION消息。- 主线程处理:
ActivityThread所在的主线程有一个Looper在不断地轮询消息队列。当它取到上述消息后,就会调用Handler的handleMessage方法。- 执行核心逻辑:在
handleMessage方法中,根据消息的what字段,分发到对应的处理方法,如handleBindApplication()或TransactionExecutor.execute()。因为此时代码已经运行在主线程,所以可以安全地进行Application和Activity的创建、生命周期回调以及所有 UI 相关的操作。总结:这个机制是 Android 中一个经典的多线程模型:后台线程(Binder 线程)负责接收耗时或阻塞的 I/O 通信,然后通过 Handler 将任务派发给主线程,由主线程负责更新 UI 和处理核心逻辑,从而保证了应用的流畅和稳定。
问题 8:什么是 ClientTransaction?Android 引入这个概念主要是为了解决什么问题?
答:
ClientTransaction(客户端事务)是 Android 较新版本中引入的一个重要概念,用于优化system_server和 App 进程之间的生命周期管理。它本质上是一个“任务包”或“指令集”。
system_server可以将多个生命周期相关的操作(如LaunchActivity、ResumeActivity、PauseActivity、StopActivity等)打包到同一个ClientTransaction对象中,然后通过一次 Binder IPC 发送给 App 进程。引入它的主要目的:
- 减少 IPC 次数,提升效率:在旧的机制中,
Activity的启动、暂停、恢复等每个状态变化都可能需要一次独立的 Binder IPC。例如,启动一个新Activity可能需要先pause旧的,再resume新的,这可能是两次 IPC。使用ClientTransaction,可以将PauseActivityItem和ResumeActivityItem打包在一起,通过一次 IPC 就完成所有指令的传递,显著降低了进程间通信的开销。- 保证操作的原子性和顺序性:将多个操作打包在一个事务中,可以更好地保证它们的执行顺序。App 端的
TransactionExecutor会按照事务中定义的顺序去执行这些生命周期回调,避免了因多次 IPC 的时序问题导致的生命周期状态混乱。- 解耦和逻辑内聚:它将生命周期管理的逻辑更好地封装起来。
system_server只负责构建和发送“任务包”,而 App 进程则负责解析和执行“任务包”,两边的职责更加清晰。简单来说,
ClientTransaction机制用一种**“批处理”**的方式,取代了过去“零散”的生命周期管理方式,使得 App 的生命周期调度更加高效、稳定和清晰。