详解 Android APP 启动流程

版权归作者所有,如有转发,请注明文章出处:cyrus-studio.github.io/blog/

APP 启动流程

app 启动流程大概如下:

arduino 复制代码
发起进程(startActivity/startService...)
↓
↓(Binder方式)
↓
system_server进程(Process.start)
↓
↓(Socket方式)
↓
Zygote进程(ZygoteInit.runSelectLoop()) 
↓
新建进程(ActivityThread.main)

ActivityThread.main() 是 Android 应用进程的入口函数。

1. 应用 → AMS(Binder)

由 ActivityManager.getService() 返回的 Binder 接口对象调用系统服务:

scss 复制代码
// 应用进程
ContextImpl.startActivity()
 └── Instrumentation.execStartActivity()
      └── ActivityManager.getService().startActivity() ← AIDL Binder 跨进程
           └── ActivityManagerService.startActivity()

通过 AIDL 实现 Binder 跨进程通信,连接系统服务。

关于 Android 中 AIDL 实现 Binder 跨进程通信 可以参考:Android中的Service与进程间通信(IPC)详解

AMS 接口实现:

aospxref.com/android-10....

2. AMS/system_server → zygote(Socket)

startActivity 最终调用到 Process.start 方法

less 复制代码
475      /**
476       * State associated with the zygote process.
477       * @hide
478       */
479      public static final ZygoteProcess ZYGOTE_PROCESS = new ZygoteProcess();
480  
481      /**
482       * Start a new process.
520       */
521      public static ProcessStartResult start(@NonNull final String processClass,
522                                             @Nullable final String niceName,
523                                             int uid, int gid, @Nullable int[] gids,
524                                             int runtimeFlags,
525                                             int mountExternal,
526                                             int targetSdkVersion,
527                                             @Nullable String seInfo,
528                                             @NonNull String abi,
529                                             @Nullable String instructionSet,
530                                             @Nullable String appDataDir,
531                                             @Nullable String invokeWith,
532                                             @Nullable String packageName,
533                                             @Nullable String[] zygoteArgs) {
534          return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
535                      runtimeFlags, mountExternal, targetSdkVersion, seInfo,
536                      abi, instructionSet, appDataDir, invokeWith, packageName,
537                      /*useUsapPool=*/ true, zygoteArgs);
538      }

aospxref.com/android-10....

ZygoteProcess.start() 中调用 startViaZygote 方法。

aospxref.com/android-10....

使用本地 UNIX socket 通信(通常路径为 /dev/socket/zygote),发送 fork 参数到 zygote socket:

scss 复制代码
// system_server 进程
ZygoteProcess.startViaZygote(...) // 使用 socket 连接 zygote

aospxref.com/android-10....

Zygote 接收 socket 后 fork 出新的应用进程:

scss 复制代码
// zygote 进程
ZygoteServer.runSelectLoop() // 接收 socket 请求

aospxref.com/android-10....

整个通信过程大概如下:

scss 复制代码
┌────────────────────────┐
│ ActivityManagerService │
└────────┬───────────────┘
         │
         ▼
┌──────────────────────┐
│ ZygoteProcess.start()│
└────────┬─────────────┘
         │ Connect
         ▼
┌───────────────────────────────────┐
│ LocalSocket("/dev/socket/zygote") │
└────────┬──────────────────────────┘
         │
         ▼
┌─────────────────────────────┐
│ ZygoteServer.runSelectLoop()│
└────────┬────────────────────┘
         │ Fork
         ▼
┌──────────────────────┐
│  子进程 (App进程)     │
└──────────────────────┘

3. 新建进程(ActivityThread.main)

从 Zygote 进程 fork APP进程 调用路径大概如下:

scss 复制代码
ZygoteInit.runSelectLoop()
└── ZygoteServer.runSelectLoop()
    └── ZygoteConnection.processOneCommand()
        └── Zygote.forkAndSpecialize()
            └── if (pid == 0) // 说明是子进程
                └── ZygoteInit.zygoteInit()
                    └── RuntimeInit.applicationInit()
                        └── Class.forName("android.app.ActivityThread")
                        └── ActivityThread.main()

aospxref.com/android-10....

ActivityThread.main() 是 APP进程 的入口点,其主要职责是为应用创建主线程(UI线程),并初始化应用的运行环境。

scss 复制代码
public static void main(String[] args) {
    // 开始性能追踪,记录"ActivityThreadMain"阶段耗时
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

    // 安装系统调用拦截器,为后续权限或行为控制做准备。
    AndroidOs.install();

    // 禁用 CloseGuard(资源泄露日志警告),避免日志污染
    CloseGuard.setEnabled(false);

    // 初始化当前用户的环境变量(如目录结构)
    Environment.initForCurrentUser();

    // 设置默认用户证书存储目录
    final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
    TrustedCertificateStore.setDefaultUserDirectory(configDir);

    // 设置进程名(显示在 ps/top 中)
    Process.setArgV0("<pre-initialized>");

    // 为主线程准备 Looper(消息循环器)
    Looper.prepareMainLooper();

    // 解析参数中是否有进程启动序列号(用于追踪性能)
    long startSeq = 0;
    if (args != null) {
        for (int i = args.length - 1; i >= 0; --i) {
            if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                startSeq = Long.parseLong(
                    args[i].substring(PROC_START_SEQ_IDENT.length()));
            }
        }
    }

    // 创建 ActivityThread 实例(应用主线程控制器)
    ActivityThread thread = new ActivityThread();

    // 启动应用:绑定 AMS,初始化 Application、Context、LoadedApk 等
    thread.attach(false, startSeq);

    // 设置主线程的 Handler(用于接收消息)
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    // (调试可选)开启日志记录主线程消息
    if (false) {
        Looper.myLooper().setMessageLogging(new
            LogPrinter(Log.DEBUG, "ActivityThread"));
    }

    // 结束性能追踪
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

    // 启动主线程消息循环(正式开始事件派发)
    Looper.loop();

    // 如果主线程意外退出,抛出异常
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

aospxref.com/android-10....

Zygote

Zygote 是 Android 系统中所有 Java 应用进程的"父进程",它通过 fork 自己 来高效创建新进程。

而 Zygote 本身并不主动创建进程,而是通过接收 AMS(SystemServer)通过 UNIX 本地 socket 发来的"启动进程"请求 来执行 fork()。

Zygote socket 的创建过程

在 Zygote 进程启动时调用 ZygoteInit.main(),创建 ZygoteServer 并执行 runSelectLoop

ini 复制代码
ZygoteServer zygoteServer = new ZygoteServer(/* isPrimaryZygote= */ true);

// 监听 socket 请求
zygoteServer.runSelectLoop(abiList);

aospxref.com/android-10....

zygoteServer.runSelectLoop() 是 Zygote 进程的主循环方法,它的作用大概如下:

java 复制代码
// com.android.internal.os.ZygoteServer.java

Runnable runSelectLoop(String abiList) {
    ...
    while (true) {
        // 1. 监听 socket
        ZygoteConnection connection = peers[i];
        ...
        // 2. 有连接请求时,读取消息
        ZygoteConnection conn = acceptCommandPeer(...);
        ...
        // 3. 解析并处理请求(如 fork 子进程)
        Runnable command = conn.processOneCommand();
        ...
        if (command != null) {
            return command;  // 子进程进入 ActivityThread.main()
        }
    }
}

aospxref.com/android-10....

ZygoteServer 构造函数中创建监听 socket(路径通常是 /dev/socket/zygote):

arduino 复制代码
178      /**
179       * @hide for internal use only.
180       */
181      public static final String PRIMARY_SOCKET_NAME = "zygote";
182  
183      /**
184       * @hide for internal use only.
185       */
186      public static final String SECONDARY_SOCKET_NAME = "zygote_secondary";

aospxref.com/android-10....

ini 复制代码
148      ZygoteServer(boolean isPrimaryZygote) {
149          mUsapPoolEventFD = Zygote.getUsapPoolEventFD();
150  
151          if (isPrimaryZygote) {
152              mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME);
153              mUsapPoolSocket =
154                      Zygote.createManagedSocketFromInitSocket(
155                              Zygote.USAP_POOL_PRIMARY_SOCKET_NAME);
156          } else {
157              mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.SECONDARY_SOCKET_NAME);
158              mUsapPoolSocket =
159                      Zygote.createManagedSocketFromInitSocket(
160                              Zygote.USAP_POOL_SECONDARY_SOCKET_NAME);
161          }
162  
163          fetchUsapPoolPolicyProps();
164  
165          mUsapPoolSupported = true;
166      }

aospxref.com/android-10....

对应设备上的路径是:

bash 复制代码
/dev/socket/zygote

这由 init.rc 脚本配置系统启动时自动创建。

进入 adb shell 执行 ls -alh /dev/socket 可以看到 zygote 文件

perl 复制代码
wayne:/ # ls -alh /dev/socket
total 0
...
srw-rw----  1 root        system         0 2025-04-21 00:14 zygote
srw-rw----  1 root        system         0 2025-04-21 00:14 zygote_secondary
  • s 是 srw------- 中的第一个字符,表示这是一个 socket 文件。

  • rw 表示这个 socket 是可读写的(给拥有者)。

UNIX 本地 socket 就像是一个可以读写的临时文件,用于在两个进程之间传输数据。

客户端发起连接:ZygoteProcess.startViaZygote

当系统需要启动新进程时,ActivityManagerService → ProcessList → ZygoteProcess.startViaZygote():

scss 复制代码
ZygoteProcess.start() →
  ZygoteProcess.startViaZygote() →
    ZygoteProcess.openZygoteSocketIfNeeded() →
      ZygoteProcess.attemptConnectionToPrimaryZygote() →
        ZygoteState.connect(address, usapPoolAddress) →
          zygoteSessionSocket.connect(zygoteSocketAddress)

aospxref.com/android-10....

Zygote 接收命令 → 执行 fork

Zygote 在 runSelectLoop() 中监听 socket:

ini 复制代码
// frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
Runnable runSelectLoop(String abiList) {
    ...
    while (true) {
        ...
        if (readyFd == mZygoteSocket.getFileDescriptor()) {
            // 有新的连接,创建新连接对象
            ZygoteConnection newConnection = acceptCommandPeer(abiList);
            mPeers.add(newConnection);
            mZygoteSocketFDs.add(newConnection.getFileDescriptor());
        } else {
            // 已有连接的数据处理
            ZygoteConnection connection = mPeers.get(index);
            final Runnable command = connection.processOneCommand(this);
            ...
        }
    }
}

aospxref.com/android-10....

然后在 processOneCommand 方法中处理 socket 请求。

最终调用:

ini 复制代码
pid = Zygote.forkAndSpecialize(...)

创建新的子进程。

aospxref.com/android-10....

ActivityThread

ActivityThread 是 Android 应用进程的 主线程(UI 线程)管理类,它是系统在启动你的 App 时,创建和调度 Application 和 Activity 的关键组件。

ActivityThread 的作用:

作用 说明
启动 Application 创建 Application 实例并调用 onCreate()
启动 Activity 创建并回调每个 Activity 的 onCreate() 等
管理 Binder 通信 和系统进程(AMS、PMS 等)通信的代理
设置类加载器 为当前应用设置合适的 ClassLoader
保持主线程运行 初始化 Looper.prepareMainLooper()、Looper.loop()

sCurrentActivityThread

ActivityThread 类中静态字段 sCurrentActivityThread 是当前 APP 的 ActivityThread 实例

aospxref.com/android-10....

sCurrentActivityThread 字段在 thread.attach(false, startSeq); 被调用时完成初始化

通过 ActivityThread 的静态方法 currentActivityThread 可以拿到 sCurrentActivityThread

csharp 复制代码
public static ActivityThread currentActivityThread() {
    return sCurrentActivityThread;
}

aospxref.com/android-10....

handleBindApplication

当 Zygote fork 出新进程后,这个进程(即你的 App)会启动并运行 ActivityThread.main(),随后 系统会通过 Binder 调用 ApplicationThread.bindApplication(),然后由主线程调用 handleBindApplication()。

scss 复制代码
system_server 进程
│
├─ ActivityManagerService.attachApplication()
│   (通过 Binder 调用 App 进程的 ApplicationThread)
│
└──► ApplicationThread.bindApplication()
        (内部类 → ActivityThread.ApplicationThread)
        ↓
    ActivityThread.sendMessage(H.BIND_APPLICATION)
        ↓
    ActivityThread.handleBindApplication()

handleBindApplication() 是 App 进程启动后最关键的初始化步骤,负责完成 Application 创建、Context 初始化、资源加载等操作。

scss 复制代码
private void handleBindApplication(AppBindData data) {
    // 设置进程名
    Process.setArgV0(data.processName);

    // 设置默认的AppContext环境
    android.ddm.DdmHandleAppName.setAppName(data.processName, UserHandle.myUserId());

    // 设置调试相关信息(例如是否开启调试)
    VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);

    // 安装类加载器(将APK的dex加载进来)
    final LoadedApk packageInfo = getPackageInfo(data.info, data.compatInfo, Context.CONTEXT_INCLUDE_CODE);

    // 创建 ContextImpl 实例
    final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);

    // 设置Instrumentation(用于后续生命周期事件的调试或监控)
    mInstrumentation = new Instrumentation();

    // 安装ContentProvider(Application.onCreate之前执行)
    installContentProviders(app, data.providers);

    // 在 makeApplication() 中,系统通过反射创建了 Application 实例,并调用了 attachBaseContext() 将 ContextImpl 赋给它,为之后调用 onCreate() 提供完整运行环境。
    Application app = data.info.makeApplication(...);

    // 调用 Application.onCreate()
    mInstrumentation.callApplicationOnCreate(app);

    // 保存 Application 实例
    mInitialApplication = app;

    // 注册广播接收器、Instrumentation、以及其他服务等
    ...
}

aospxref.com/android-10....

mClassLoader

ActivityThread 通过 mPackages 缓存所有已加载的应用包信息(LoadedApk 对象),每个 LoadedApk 封装了当前 App 的 mClassLoader,用于动态加载该应用的类和资源。

ActivityThread、mPackages、LoadedApk、mClassLoader 之间的关系

javascript 复制代码
ActivityThread(整个App主进程管理器)
  └── mPackages: Map<String, WeakReference<LoadedApk>>(已加载的 APK 缓存表)
                       ↓
               LoadedApk(当前应用的APK加载表示)
                       ↓
               mClassLoader(当前的类加载器)

mPackages

aospxref.com/android-10....

LoadedApk 中的 mClassLoader

aospxref.com/android-10....

Looper、MessageQueue、Handler

Looper 是一个"消息循环器",用于循环读取 MessageQueue 中的消息,并分发给目标 Handler 处理。

  • 每个线程只能有一个 Looper。

  • 主线程默认创建了 Looper 并调用了 Looper.loop()。

MessageQueue 是 "消息队列",用于存储 Handler 发送过来的消息。

  • 消息按时间顺序排列。

  • Looper 会不断从中取出消息执行。

Handler 是"消息的发送和处理者"。

  • 用来发送消息或任务到 MessageQueue。

  • 当 Looper 取到消息后,会回调该消息对应的 Handler 的 handleMessage() 方法。

三者一起构成了 Android 的异步消息机制,是实现 UI 更新、线程通信、定时任务的基础。

如果没有它们:

  • 子线程直接更新 UI,会导致 异常崩溃。

  • 没有机制将任务从子线程安全地"切换"到主线程。

整体结构图:

scss 复制代码
Looper.loop()
   ↓
MessageQueue.next() ←(epoll_wait 阻塞)
   ↑               ↑
   │               └── ← 唤醒 ← 插入新消息 Handler.post()/sendMessage()
   │
   └── 有消息 → 返回 Message → 交给 Handler 处理

Looper.loop()

ActivityThread.main 函数中调用 Looper.loop() 后,会不断地:

  1. 等待消息队列(MessageQueue)中的任务

  2. 有消息(如生命周期调用、Handler消息、点击事件)立刻醒来执行

  3. 没消息就阻塞休息(不消耗 CPU)

从 Looper.loop() 到 native epoll_wait()

scss 复制代码
Looper.loop()
  ↓
MessageQueue.next()
  ↓
nativePollOnce() (JNI)
  ↓
android_os_MessageQueue.cpp
  ↓
Looper::pollOnce()
  ↓
epoll_wait()

1. Looper.loop() 中的 MessageQueue.next()

这是整个事件循环的核心。这里的 queue.next():

  • 会阻塞当前线程

  • 等待新的消息进入 MessageQueue

  • 等有消息时返回 Message,由 Handler 执行

java 复制代码
public static void loop() {
    // 获取当前线程的 Looper 实例,主线程默认会创建 Looper.prepareMainLooper()
    final Looper me = myLooper();

    // 获取消息队列 MessageQueue,内部通过 native 层 epoll 实现阻塞等待消息
    final MessageQueue queue = me.mQueue;

    // 主线程事件循环
    for (;;) {
        // 从消息队列中取出下一个 Message,如果没有则阻塞等待
        Message msg = queue.next(); // might block
        if (msg == null) {
            // null 表示 Looper 已退出(如调用了 quit()),结束 loop
            return;
        }

        // 正常取到消息后,调用目标 Handler 派发消息
        // target 即发送该消息时绑定的 Handler
        msg.target.dispatchMessage(msg);

        // 消息处理完成后,释放消息对象回池中复用
        msg.recycleUnchecked();
    }
}

aospxref.com/android-10....

2. MessageQueue.next() → nativePollOnce()

在 MessageQueue.next() 中,调用了 JNI 方法 nativePollOnce() 来监听消息和事件:

aospxref.com/android-10....

3. android_os_MessageQueue_nativePollOnce

scss 复制代码
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj, jlong ptr, jint timeoutMillis)  {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->pollOnce(timeoutMillis);
}

aospxref.com/android-10....

pollOnce() 会调用 Looper::pollOnce():

arduino 复制代码
void NativeMessageQueue::pollOnce(int timeoutMillis) {
    mLooper->pollOnce(timeoutMillis);
}

aospxref.com/android-10....

4. Looper::pollOnce() 最终调用 epoll_wait

Looper::pollOnce 中调用 pollInner

arduino 复制代码
int Looper::pollOnce(int timeoutMillis, ...) {
    int result = pollInner(timeoutMillis);
    ...
}

aospxref.com/android-10....

核心方法:

arduino 复制代码
int Looper::pollInner(int timeoutMillis) {
    int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    ...
}

aospxref.com/android-10....

epoll_wait() 是一个高效的内核级阻塞函数(syscall),用于等待一个或多个文件描述符上发生 I/O 事件,从而实现事件驱动的非阻塞编程。

参数说明:

  • mEpollFd.get():获取 epoll 实例的文件描述符(用于监控事件)

  • eventItems:一个epoll_event数组,用来存储发生的事件

  • EPOLL_MAX_EVENTS:一次最多返回多少个事件(常量)

  • timeoutMillis:阻塞的时间,单位是毫秒(例如:-1 表示无限阻塞,0 表示立即返回)

Looper.loop() 阻塞主线程其实是通过 epoll_wait() 来 高效监听多个事件源(管道、Binder、ANR 信号、socket 等),没事件就睡觉,有事件就立刻醒来处理,既节能又响应及时,这就是 Android 主线程调度的精髓!

5. 有消息/事件来了怎么唤醒?

当我们在子线程或者主线程执行如下代码时:

ini 复制代码
handler.sendMessage(Message.obtain().apply { what = 100 })

它最终会调用 MessageQueue.enqueueMessage(),这个方法里有这么一句关键代码:

scss 复制代码
nativeWake(mPtr);

aospxref.com/android-10....

对应到 native 层(frameworks/base/core/jni/android_os_MessageQueue.cpp):

scss 复制代码
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->wake();
}

aospxref.com/android-10....

aospxref.com/android-10....

然后进入 Looper::wake() 中(frameworks/base/core/jni/android_os_MessageQueue.cpp → Looper.cpp):

arduino 复制代码
void Looper::wake() {
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, "w", 1));
}

aospxref.com/android-10....

这句 write(mWakeEventFd, "w", 1); 就是关键:👉 往 epoll 注册的 eventfd 写数据,从而唤醒 epoll_wait。

相关推荐
后端码匠3 小时前
MySQL 8.0安装(压缩包方式)
android·mysql·adb
梓仁沐白4 小时前
Android清单文件
android
董可伦6 小时前
Dinky 安装部署并配置提交 Flink Yarn 任务
android·adb·flink
每次的天空7 小时前
Android学习总结之Glide自定义三级缓存(面试篇)
android·学习·glide
恋猫de小郭7 小时前
如何查看项目是否支持最新 Android 16K Page Size 一文汇总
android·开发语言·javascript·kotlin
flying robot9 小时前
小结:Android系统架构
android·系统架构
xiaogai_gai9 小时前
有效的聚水潭数据集成到MySQL案例
android·数据库·mysql
鹅鹅鹅呢10 小时前
mysql 登录报错:ERROR 1045(28000):Access denied for user ‘root‘@‘localhost‘ (using password Yes)
android·数据库·mysql
在人间负债^10 小时前
假装自己是个小白 ---- 重新认识MySQL
android·数据库·mysql
Unity官方开发者社区10 小时前
Android App View——团结引擎车机版实现安卓应用原生嵌入 3D 开发场景
android·3d·团结引擎1.5·团结引擎车机版