详解 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。

相关推荐
思想觉悟1 小时前
使用AndroidStudio阅读源码
android
智驾1 小时前
HarmonyOS 是 Android 套壳嘛?
android·harmonyos·替代·套壳
longzekai1 小时前
【重学Android】03.高版本 Android Studio 不能使用引用库资源ID的问题
android·ide·android studio
YSoup2 小时前
2025深圳中兴通讯安卓开发社招面经
android
JohnYan2 小时前
工作笔记-应用磁盘无感扩容
linux·后端·操作系统
智践行2 小时前
机器人操作系统ROS2之理解动作
人工智能·操作系统
ufo00l2 小时前
ViewModel 在什么时候被销毁
android
声知视界2 小时前
音视频基础能力之 Android 音频篇 (六):音频编解码到底哪家强?
android·音视频开发
ufo00l2 小时前
讲述EventBus和RxBus的工作模式以及和LiveData消息总栈的优劣势
android
玄之宵2 小时前
Android 回显
android·java·开发语言