首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

详解 Android APP 启动流程

  • 25-04-25 12:42
  • 2598
  • 11007
juejin.cn

版权归作者所有,如有转发,请注明文章出处: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 接口实现:

word/media/image1.png 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 方法。

word/media/image2.png 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(""); // 为主线程准备 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 实例

word/media/image3.png aospxref.com/android-10.…

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

word/media/image4.png

通过 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

word/media/image5.png aospxref.com/android-10.…

LoadedApk 中的 mClassLoader

word/media/image6.png 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() 来监听消息和事件:

word/media/image7.png 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(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(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。

注:本文转载自juejin.cn的CYRUS_STUDIO的文章"https://juejin.cn/post/7496397558359081014"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

140
Android
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top