Activity 启动流程

54 阅读12分钟

原文链接:blog.csdn.net/u010257931/…

Android App 启动后续流程

在深入细节之前,我们必须先理解整个流程背后的两个核心设计原则:

  1. 进程隔离与通信: App 进程和 system_server 进程是完全独立的。它们之间唯一的沟通方式是 Binder IPC (Inter-Process Communication)。这就像两个不同国家的人打电话,必须通过一个共有的电话网络(Binder 驱动)和遵循特定协议(AIDL 接口)。
  2. 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()
  • 关键代码:
    1. Looper.prepareMainLooper(): 这是 App 的心跳之源。它为当前线程(即主线程)创建一个 Looper 和一个 MessageQueue,让它具备了处理消息循环的能力。没有这一步,App 就会执行完 main 方法后直接退出。
    2. ActivityThread thread = new ActivityThread(): 创建 ActivityThread 实例。这个类是 App 进程中实际的“主宰”,它管理着进程中所有 ActivityService 等组件的生命周期。
    3. 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: ActivityThread vs. 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 进程的 ApplicationThreadbindApplication 方法,并把所有应用信息作为参数传过去。
  • 目的 (Why)? 这是 ATMS 在下达第一个正式指令:“报到已收到。现在,根据我给你的这些信息,把你的 Application 对象创建出来。”

Step 2.3: 线程切换 - The Handler Switch

  • 发生了什么? bindApplication 方法是在 App 进程的一个 Binder 线程中执行的(Binder 调用总是在独立的线程池中处理)。但创建 Application 涉及到后续的 UI 操作,必须在主线程完成。
  • 关键动作: ApplicationThread 立即将所有接收到的参数打包成一个 AppBindData 对象,然后调用 sendMessage(H.BIND_APPLICATION, data)HActivityThread 内部的一个 Handler,它与主线程的 Looper 绑定。这样,消息就被发送到了主线程的消息队列中。

Step 2.4: 主线程执行 - handleBindApplication()

  • 发生了什么? 主线程的 Looper 从消息队列中取出 BIND_APPLICATION 消息,并调用 ActivityThreadhandleBindApplication 方法来处理。
  • 关键动作:
    1. data.info.makeApplicationInner(...): 这是创建 Application 的核心。data.info 是一个 LoadedApk 对象,它代表了已加载的 APK 信息。
    2. 内部通过 mInstrumentation.newApplication(...) 来实例化 Application
    3. app.attach(context): 将创建好的 Application 实例与一个 ContextImpl 绑定,使其具备完整的上下文能力。
    4. mInstrumentation.callApplicationOnCreate(app): 这个调用最终会触发我们所熟知的 Application.onCreate()

Key Player Spotlight: Instrumentation Instrumentation 类是 Android 的一个“钩子”机制。系统并不直接 new Application()new Activity(),而是通过 Instrumentation 对象来创建。这使得测试框架(如 Espresso)可以替换掉默认的 Instrumentation,从而在测试时注入模拟(Mock)的 ApplicationActivity,实现对应用的完全控制。


阶段三:首个 Activity 的创建 (事务驱动)

Step 3.1 & 3.2: 系统准备启动 Activity - realStartActivityLocked()

  • 发生了什么? 回到 system_server 进程。在 attachApplicationLocked 方法的后半部分,系统确认 App 进程已经准备好后,就开始启动入口 Activity。经过 mAtmInternal.attachApplication() -> ... 等一系列调用,最终到达 ActivityStackSupervisor.realStartActivityLocked()
  • 关键动作: Android 现代版本引入了 ClientTransaction(客户端事务) 的概念来管理生命周期。系统不再是零散地发送 pauseresume 等命令,而是把一系列操作打包成一个“事务”。
    • ClientTransaction.obtain(...): 创建一个事务。
    • clientTransaction.addCallback(LaunchActivityItem.obtain(...)): 向事务中添加一个 LaunchActivityItem 回调项。这个回调项包含了启动新 Activity 所需的所有信息(Intent、ActivityInfo 等)。
    • clientTransaction.setLifecycleStateRequest(ResumeActivityItem.obtain()): 设置这个事务执行完毕后,Activity 应该达到的最终状态(Resumed)。

Step 3.3 - 3.5: 发送并执行事务

  • 发生了什么?
    1. mService.getLifecycleManager().scheduleTransaction(clientTransaction): ATMS 通过 Binder 将这个打包好的“事务”发送给 App 进程。
    2. 又一次线程切换: App 进程的 ApplicationThread 在 Binder 线程接收到 scheduleTransaction 调用,然后再次通过 Handler 发送 EXECUTE_TRANSACTION 消息给主线程。
    3. 主线程执行: ActivityThreadhandleMessage 收到消息后,调用 mTransactionExecutor.execute(transaction)

Step 3.6 - 3.8: Activity 的诞生

  • 发生了什么? TransactionExecutor 开始按顺序执行事务中的项目。
  • 关键动作:
    1. executeCallbacks(): 执行事务中的回调列表。我们的 LaunchActivityItem 就在其中。
    2. LaunchActivityItem.execute(): 这个回调被执行,它调用 client.handleLaunchActivity() (client 就是 ActivityThread)。
    3. 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 以及界面的测量、布局、绘制将由 WindowManagerView 系统接管。

好的,遵照你的要求,我在上次的基础上,结合两篇文章的完整流程,为你补充了更多、更深入的面试题和答案。

这些问题不仅覆盖了“是什么”和“怎么做”,更侧重于考察“为什么这么设计”,这在高级工程师的面试中尤为重要。


补充面试题与详解 (高级/资深)

问题 5:Zygote fork 出新进程后,App 进程是如何“主动”与 SystemServer 建立联系的?这个“握手”过程的关键是什么?

答: Zygote fork出的新进程并不是被动等待 SystemServer 的指令,而是会主动反向注册SystemServer,这个过程可以理解为一次“握手”或“报到”。

  1. 入口与初始化:新进程的入口是 ActivityThread.main() 方法。在该方法中,会创建主线程的 Looper,并实例化 ActivityThread 类。
  2. 关键的 attach() 调用ActivityThread 实例创建后,会立即调用其 attach() 方法。
  3. 发起 Binder IPCattach() 方法的核心是,通过 Binder IPC 调用 ActivityTaskManagerServiceattachApplication() 方法。
  4. 传递通信信物:在这次调用中,最关键的参数是 mAppThreadmAppThreadActivityThread 的一个内部类 ApplicationThread 的实例,它是一个 Binder 对象。当它被传递给 SystemServer 后,SystemServer 就持有了这个 App 进程的 Binder 代理。

这个“握手”过程的关键在于:App 进程通过 attachApplication() 调用,将自己的通信能力(ApplicationThread 这个 Binder 服务端)注册给了 SystemServer。从此以后,SystemServer 就可以通过这个 Binder 代理,反向调用 App 进程中的方法,从而下达创建 ApplicationActivity 的指令。没有这次主动的“握手”,SystemServer 就无法与新创建的 App 进程建立通信。


问题 6:在 App 进程中,Application.onCreate()Activity.onCreate() 的调用是由两次不同的 Binder 通信触发的,请分别描述这两次通信的发起者、接收者和核心目的。

答: 是的,ApplicationActivity 的创建流程是分离的,由两次独立的、从 SystemServer 到 App 进程的 Binder 通信驱动。

第一次通信:创建 Application

  • 发起者SystemServer 进程中的 ActivityTaskManagerService (ATMS)。
  • 触发时机:在 ATMS 的 attachApplicationLocked() 方法中,当它收到了 App 进程的“握手”请求后。
  • 调用的方法:通过 App 进程的 Binder 代理,调用 IApplicationThread.bindApplication()
  • 接收者:App 进程中的 ApplicationThread(运行在 Binder 线程)。
  • 核心目的:指令 App 进程加载应用数据,创建 ContextImplApplication 对象,并最终回调 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 线程中直接创建 ApplicationActivity 并操作 UI,会引发严重的线程安全问题和程序崩溃。

工作机制如下:

  1. Binder 线程接收SystemServer 调用 bindApplicationscheduleTransaction 后,App 进程中一个空闲的 Binder 线程会接收到这个请求并开始执行 ApplicationThread 中的对应方法。
  2. 打包并发送消息:在 ApplicationThread 的方法中,它并不会执行实际的业务逻辑。而是将所有参数数据打包(例如打包成 AppBindData 对象或直接传递 ClientTransaction 对象),然后调用 ActivityThread 中的 HandlermH)发送一个消息到主线程的消息队列。
    • 对于 bindApplication,发送的是 H.BIND_APPLICATION 消息。
    • 对于 scheduleTransaction,发送的是 H.EXECUTE_TRANSACTION 消息。
  3. 主线程处理ActivityThread 所在的主线程有一个 Looper 在不断地轮询消息队列。当它取到上述消息后,就会调用 HandlerhandleMessage 方法。
  4. 执行核心逻辑:在 handleMessage 方法中,根据消息的 what 字段,分发到对应的处理方法,如 handleBindApplication()TransactionExecutor.execute()。因为此时代码已经运行在主线程,所以可以安全地进行 ApplicationActivity 的创建、生命周期回调以及所有 UI 相关的操作。

总结:这个机制是 Android 中一个经典的多线程模型:后台线程(Binder 线程)负责接收耗时或阻塞的 I/O 通信,然后通过 Handler 将任务派发给主线程,由主线程负责更新 UI 和处理核心逻辑,从而保证了应用的流畅和稳定。


问题 8:什么是 ClientTransaction?Android 引入这个概念主要是为了解决什么问题?

答: ClientTransaction(客户端事务)是 Android 较新版本中引入的一个重要概念,用于优化 system_server 和 App 进程之间的生命周期管理。

它本质上是一个“任务包”或“指令集”system_server 可以将多个生命周期相关的操作(如 LaunchActivityResumeActivityPauseActivityStopActivity 等)打包到同一个 ClientTransaction 对象中,然后通过一次 Binder IPC 发送给 App 进程。

引入它的主要目的:

  1. 减少 IPC 次数,提升效率:在旧的机制中,Activity 的启动、暂停、恢复等每个状态变化都可能需要一次独立的 Binder IPC。例如,启动一个新 Activity 可能需要先 pause 旧的,再 resume 新的,这可能是两次 IPC。使用 ClientTransaction,可以将 PauseActivityItemResumeActivityItem 打包在一起,通过一次 IPC 就完成所有指令的传递,显著降低了进程间通信的开销。
  2. 保证操作的原子性和顺序性:将多个操作打包在一个事务中,可以更好地保证它们的执行顺序。App 端的 TransactionExecutor 会按照事务中定义的顺序去执行这些生命周期回调,避免了因多次 IPC 的时序问题导致的生命周期状态混乱。
  3. 解耦和逻辑内聚:它将生命周期管理的逻辑更好地封装起来。system_server 只负责构建和发送“任务包”,而 App 进程则负责解析和执行“任务包”,两边的职责更加清晰。

简单来说ClientTransaction 机制用一种**“批处理”**的方式,取代了过去“零散”的生命周期管理方式,使得 App 的生命周期调度更加高效、稳定和清晰。