Android 应用启动背后的故事:从点击图标到界面显示的奇妙旅程

87 阅读7分钟

用通俗易懂的故事和关键代码片段,来理解这篇 Android 应用程序启动源码分析的精华。想象一下你点击手机桌面上的一个App图标(比如“计算器”),背后发生了一场精密的“快递接力赛”。

​核心故事:一次点击引发的“快递”接力​

  1. ​你下单了!(Launcher -> startActivity)​

    • 你点击了计算器图标(Launcher 应用)。

    • Launcher 说:“用户想用计算器!我得通知调度中心(ActivityManagerService,简称 AMS)。” 它创建了一个 Intent(快递单),上面写着“收件人:计算器App的主界面(MainActivity)”,并贴上了“请用新卡车运送” (FLAG_ACTIVITY_NEW_TASK) 的标签。

    • ​代码体现 (Launcher.java):​

      java
      Copy
      void startActivitySafely(Intent intent, Object tag) {
          intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 贴上“新卡车”标签
          try {
              startActivity(intent); // 发出订单
          } catch ... // 处理异常
      }
      
    • 这个 startActivity(intent) 调用最终会通过 Activity.startActivityForResult -> Instrumentation.execStartActivity

  2. ​快递员接单,联系调度中心 (Launcher -> AMS)​

    • Launcher 内部的快递员 (Instrumentation) 拿起电话(Binder IPC),打给 AMS 的前台 (ActivityManagerProxy):“嘿调度中心,有订单!用户要启动计算器App的主界面,用新卡车!”

    • ​代码体现 (Instrumentation.java -> ActivityManagerProxy.java):​

      java
      Copy
      // Instrumentation.execStartActivity
      int result = ActivityManagerNative.getDefault() // 拿到AMS的代理
              .startActivity(whoThread, intent, ...); // 调用AMS代理的方法
      
      // ActivityManagerProxy.startActivity (代理,负责打包数据跨进程发送)
      public int startActivity(...) throws RemoteException {
          Parcel data = Parcel.obtain(); // 打包数据
          ... // 写入intent等信息
          mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0); // Binder跨进程发送
          ... // 读取结果
      }
      
  3. ​调度中心处理订单 (AMS)​

    • AMS (真正的调度中心) 收到订单 (ActivityManagerService.startActivity)。

    • AMS 查看订单 (Intent):“计算器App?它的主界面?用新卡车...明白了。” 它查数据库 (PackageManagerService) 确认这个App的信息 (ActivityInfo)。

    • AMS 发现计算器App还没运行:“需要一辆新卡车(新进程)!” 它在内部记录本 (ActivityStack) 上创建了一个新任务 (TaskRecord) 来放这个主界面。

    • AMS 对正在前台工作的 Launcher 说:“Launcher,用户点了别的App,你先去后面休息室待命(进入Paused状态),等会儿可能需要你回来。”

    • ​代码体现 (ActivityManagerService.java / ActivityStack.java):​

      java
      Copy
      // AMS.startActivity -> ActivityStack.startActivityMayWait
      // ... 解析Intent,找到目标ActivityInfo ...
      synchronized (mService) {
          ... // 权限等检查
          int res = startActivityLocked(...); // 核心启动逻辑
      }
      
      // ActivityStack.startActivityLocked -> startActivityUncheckedLocked
      if (需要新Task) {
          r.task = new TaskRecord(...); // 创建新任务记录
          newTask = true;
      }
      resumeTopActivityLocked(prev); // 尝试让最顶上的Activity(新任务)运行,这会先暂停当前Activity
      
      // 暂停Launcher (Step 10-11)
      if (mResumedActivity != null) { // Launcher还在前台
          startPausingLocked(userLeaving, false); // 告诉Launcher暂停
      }
      
  4. ​通知前台休息 (AMS -> Launcher)​

    • AMS 打电话 (Binder IPC) 给 Launcher 内部的专属接线员 (ApplicationThread):“Launcher,请暂停你当前的工作!”

    • Launcher 的专属接线员 (ApplicationThread) 收到消息后,立刻写了个便签 (Message),交给 Launcher 的主管家 (H - Handler):“老板,调度中心让你暂停一下。”

    • Launcher 的主管家 (H) 看到便签后,对 Launcher 说:“老板,按流程执行 onPause() 吧。”

    • Launcher 执行完 onPause() 后,通过它的专属接线员 (ApplicationThread) 回电话给 AMS:“调度中心,我已经暂停好了 (activityPaused)。”

    • ​代码体现 (ApplicationThread.java -> ActivityThread.java -> ActivityManagerProxy.java):​

      java
      Copy
      // ApplicationThread.schedulePauseActivity (收到AMS暂停请求)
      public final void schedulePauseActivity(...) {
          queueOrSendMessage(H.PAUSE_ACTIVITY, token, ...); // 发送消息给主线程Handler
      }
      
      // H (Handler) 处理 PAUSE_ACTIVITY 消息
      case PAUSE_ACTIVITY: handlePauseActivity(...);
      
      // ActivityThread.handlePauseActivity
      performPauseActivity(...); // 调用Activity.onPause()
      ActivityManagerNative.getDefault().activityPaused(token, state); // 通知AMS暂停完成
      
  5. ​调度中心派出新卡车 (AMS 启动新进程)​

    • AMS 收到 Launcher 暂停完成的通知。

    • AMS 说:“好了,Launcher 让位了。现在该启动计算器App的新卡车了!”

    • AMS 呼叫车辆调配中心 (Zygote):“请派一辆新车,司机就用 android.app.ActivityThread 这个培训手册,车牌号(进程名)叫 shy.luo.activity(计算器App的包名)。”

    • ​代码体现 (ActivityManagerService.java):​

      java
      Copy
      // ActivityStack.realStartActivityLocked 之前检查进程
      ProcessRecord app = mService.getProcessRecordLocked(r.processName, ...);
      if (app == null || app.thread == null) { // 进程不存在或没准备好
          mService.startProcessLocked(r.processName, ...); // 启动新进程!
          return;
      }
      
      // AMS.startProcessLocked (核心)
      int pid = Process.start("android.app.ActivityThread", ...); // 请求Zygote fork新进程
      
  6. ​新车出厂,司机报到 (新进程初始化)​

    • Zygote 成功分叉 (fork) 出一个新进程。

    • 在这个新进程里,android.app.ActivityThread 的 main() 方法被调用。这是​​每个App进程的主心骨​​。

    • main() 方法创建了 ActivityThread 实例 (mAppThread 是其内部类 ApplicationThread),并建立了消息循环 (Looper.loop())。

    • 新进程的 ActivityThread 主动打电话给 AMS:“调度中心你好,我是新车 shy.luo.activity 的专属接线员 (ApplicationThread),我的电话号码 (IBinder) 是 mAppThread,以后有事请打这个电话找我!”

    • ​代码体现 (ActivityThread.java):​

      java
      Copy
      public static void main(String[] args) {
          ... // 初始化
          ActivityThread thread = new ActivityThread();
          thread.attach(false); // 重要!向AMS报到
          Looper.prepareMainLooper(); // 准备消息循环
          ... // 其他初始化
          Looper.loop(); // 开始处理消息(无限循环)
      }
      
      private void attach(boolean system) {
          ... // 非系统应用
          IActivityManager mgr = ActivityManagerNative.getDefault();
          mgr.attachApplication(mAppThread); // 向AMS注册本进程的ApplicationThread
      }
      
  7. ​调度中心下达送货指令 (AMS -> 新进程)​

    • AMS 收到新进程的报到电话 (attachApplication)。

    • AMS 很高兴:“新车 shy.luo.activity 的接线员 (ApplicationThread) 上线了!正好有任务给他。”

    • AMS 立刻通过 Binder IPC 打电话给新进程的专属接线员 (ApplicationThread):“嘿,新车司机!你的第一个任务:启动你车上的 shy.luo.activity.MainActivity 这个货(Activity)!”

    • ​代码体现 (ActivityManagerService.java -> ApplicationThreadProxy.java):​

      java
      Copy
      // AMS.attachApplicationLocked (收到新进程报到)
      ... // 找到对应的ProcessRecord app
      app.thread = thread; // 保存新进程的ApplicationThread
      ... // 检查
      if (有Activity等待在此进程启动) { // 就是我们的MainActivity
          if (mMainStack.realStartActivityLocked(hr, app, true, true)) {
              ... // 成功启动
          }
      }
      
      // ActivityStack.realStartActivityLocked (核心)
      app.thread.scheduleLaunchActivity(...); // 通过新进程的ApplicationThread发送启动指令
      
  8. ​新车司机拆包安装,展示货物 (新进程启动 Activity)​

    • 新进程的专属接线员 (ApplicationThread) 收到 scheduleLaunchActivity 指令。

    • 他立刻写了个便签 (Message) 交给本进程的主管家 (H):“老板,调度中心让我们启动 MainActivity!”

    • 主管家 (H) 看到 LAUNCH_ACTIVITY 便签,调用 handleLaunchActivity

    • handleLaunchActivity 做了几件大事:

      • ​找零件:​​ 通过 ClassLoader 加载 MainActivity.class
      • ​造主体:​​ 用反射 (mInstrumentation.newActivity) 创建 MainActivity 对象。
      • ​装环境:​​ 创建 Context(应用上下文),调用 activity.attach(...) 给 MainActivity 装上。
      • ​启动生命:​​ 调用 mInstrumentation.callActivityOnCreate(activity, r.state) -> 触发 MainActivity.onCreate(Bundle savedInstanceState)!​​App 界面开始绘制!​
      • ​展示给用户:​​ 接着调用 handleResumeActivity -> 触发 onStart() 和 onResume() -> ​​界面可见并可交互!​
    • ​代码体现 (ActivityThread.java):​

      java
      Copy
      // H.handleMessage
      case LAUNCH_ACTIVITY: {
          ActivityClientRecord r = (ActivityClientRecord)msg.obj;
          handleLaunchActivity(r, null); // 处理启动
      }
      
      // ActivityThread.handleLaunchActivity
      Activity a = performLaunchActivity(r, customIntent); // 创建、初始化、onCreate
      if (a != null) {
          handleResumeActivity(r.token, false, r.isForward); // 触发onStart, onResume
      }
      
      // ActivityThread.performLaunchActivity (核心)
      java.lang.ClassLoader cl = ...;
      activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent); // 反射创建Activity实例
      ... // 创建Context
      activity.attach(...); // 绑定上下文等
      if (r.state != null) { // 恢复保存的状态(如果有)
          mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
      }
      mInstrumentation.callActivityOnCreate(activity, r.state); // 调用onCreate!!!
      ... // 其他生命周期
      

​总结一下这场“快递”接力的核心要点:​

  1. ​起点(用户):​​ 点击图标(Launcher)。

  2. ​订单处理(Launcher):​​ 创建 Intent,调用 startActivity

  3. ​跨进程通知(Binder IPC):​​ Launcher -> ActivityManagerService (AMS)。

  4. ​全局调度(AMS):​

    • 解析 Intent,找到目标 Activity 和 App
    • 决定启动新进程(因为 FLAG_ACTIVITY_NEW_TASK + App未运行)。
    • 暂停当前 Activity (Launcher)。
    • 请求 Zygote fork 新进程。
  5. ​新进程初始化:​​ ActivityThread.main() 运行,创建主线程消息循环,向 AMS 注册 ApplicationThread(进程专属对讲机)。

  6. ​启动指令下达(Binder IPC):​​ AMS -> 新进程的 ApplicationThread

  7. ​目标 Activity 诞生与展示(新进程):​

    • ApplicationThread 通知主线程 Handler (H)。
    • H 调用 handleLaunchActivity / performLaunchActivity
    • ​核心动作:​​ 反射创建 Activity 实例 -> 绑定上下文 (attach) -> 调用 onCreate() -> 调用 onResume()
  8. ​终点(用户):​​ 看到并可以操作新 App (MainActivity) 的界面。

​关键角色回顾:​

  • Launcher:​​ 起点,订单发起者。
  • ActivityManagerService (AMS):​​ 中央调度中心,管理所有 ActivityTask 和进程。
  • Instrumentation:​​ 监控工具,负责调用 Activity 的生命周期方法。
  • ActivityThread:​​ ​​每个 App 进程的主线程核心​​,管理本进程内的 Activity 生命周期、消息循环 (Looper/Handler)。它的 main() 是 App 进程的入口。
  • ApplicationThread:​​ ActivityThread 的内部类,是一个 Binder 对象(IApplicationThread.Stub)。它是 ​​App 进程与 AMS 通信的桥梁​​。AMS 通过它向 App 进程发送指令。
  • H (Handler):​​ ActivityThread 的内部类,负责在主线程处理来自 ApplicationThread(即来自 AMS)的消息(如 LAUNCH_ACTIVITYPAUSE_ACTIVITY)。
  • Binder IPC:​​ 贯穿始终的跨进程通信机制。

通过这个“快递接力”的故事和关键代码节点的说明,应该能清晰地理解点击 App 图标到界面显示背后复杂的系统级协作过程了!这就是 Android 应用程序启动的源码精髓。