第 1 篇:App 发起启动
文章中代码基于 Android 15 版本,旨在串流程,打破遇到问题无从下手的窘境。
结论:startActivity 没抛异常 ≠ 界面已经出来。只说明 ATMS 已接单,窗口还要等事务下发、onCreate/onResume、attach、绘制。本文只讲:这一行调用怎么进到系统、怎么验证「请求已进 ATMS」、以及别把「接单」当「已创建窗口」。
堆栈格式与 printStackTrace() 一致:自上而下 = 从当前/最内层到根/入口,便于对照日志 trace 分析。
一、你遇到的现象 / 问题
-
点了图标或按钮, startActivity没抛异常,但界面迟迟不出或「没反应」。 -
想确认:请求到底有没有进 system_server? 目标 Activity 有没有入栈?
→ 下面用调用堆栈表示「进系统」的路径(与 总览 同一格式);再往后是可复现操作 + 真实示例,照着做即可验证。
二、主流程:App 一行调用 → ATMS(调用堆栈)
格式同 printStackTrace():上=当前/最内层,下=根/入口。进程块用 ▼ 标题;跨进程用一行标出「调用方 ──► 被调方」和接口名。
▼ system_server · ATMS 入口(决策点 1 起点) at ActivityStarter.execute at ActivityTaskManagerService.startActivityAsUser // 内为 getActivityStartController().obtainStarter(...).setCaller(...).execute() at ActivityTaskManagerService.startActivityBinder: App ──► system_server (IActivityTaskManager.startActivity)▼ App 进程 at ActivityTaskManager.getService().startActivity at Instrumentation.execStartActivity at Activity.startActivityForResult at Activity.startActivity
指路:真正进系统的是 Instrumentation.execStartActivity → Binder → ATMS;startActivityAsUser 后交 obtainStarter(...).execute(),即决策点 1(见总览)。
三、三种入口汇入 ATMS(调用堆栈)
下面列出最常用的三种入口(对应 ATMS 的 startActivity/startActivityAsUser 与 startActivityIntentSender);ATMS 侧还有 startActivities(批量)、startActivityFromRecents(最近任务)、startActivityAndWait、startActivityWithConfig 等接口,用于其他场景,此处不展开。以下调用栈为典型路径,与 AOSP 代码一致;实际栈可能因版本或分支略有差异。
入口 1:Activity 内 startActivity
▼ system_server at ActivityTaskManagerService.startActivityAsUser at ActivityTaskManagerService.startActivity ...Binder: App ──► system_server (IActivityTaskManager.startActivity)▼ App 进程 at ActivityTaskManager.getService().startActivity at Instrumentation.execStartActivity at Activity.startActivityForResult at Activity.startActivity
入口 2:PendingIntent.send(不经过当前 App 的 Instrumentation)
▼ system_server at ActivityTaskManagerService.startActivityIntentSender ...Binder: 调用方进程 ──► system_server (startActivityIntentSender)▼ 调用方进程(如 Notification、其他 App) at IntentSender.sendIntent at PendingIntent.send
入口 3:非 Activity 的 Context 内 startActivity(如 Application、Service)
▼ system_server at ActivityTaskManagerService.startActivityAsUser at ActivityTaskManagerService.startActivity ...Binder: App ──► system_server (IActivityTaskManager.startActivity)▼ App 进程(如 Service 所在进程) at ActivityTaskManager.getService().startActivity at Instrumentation.execStartActivity at ContextImpl.startActivity at Service.startActivity / Application 内调用等
指路:入口 1、3 在 system_server 侧均为 startActivity / startActivityAsUser,区别在调用方栈(Activity 内 vs ContextImpl/Service);入口 2 走 startActivityIntentSender,不经过当前 App 的 Instrumentation。
四、为什么会误判?(原理只讲一层)
- 「调用成功」
:Binder 返回非致命码(如 START_SUCCESS), checkStartActivityResult不抛 → 只表示 ATMS 已接单。 - 「已创建窗口」
:目标进程已执行 handleResume、ViewRootImpl、finishDrawing 等(决策点 2/3)。
中间还有:进程创建、scheduleTransaction 下发、onCreate/onResume、attach 窗口、Transition。所以没抛异常不能推出「系统已经去建窗口」,排查「点了没反应」要看 dumpsys / 事务是否下发。
五、可复现操作 + 真实示例
环境与前提
- 环境
:AOSP 源码树、已连接设备(真机或模拟器)、 adb可用。 - 前提
:已安装本仓库示例 App startactivitydemo(见下)。
真实示例 App:startactivitydemo
- 位置
:本仓库示例工程 startactivitydemo(Gradle 工程,可与文档一起发布);若需随系统镜像发布,可将该工程放入 AOSP 树并在 product mk 中加入对应模块名。 - 行为
:主界面一个按钮「启动 SecondActivity」;点击即 startActivity(new Intent(MainActivity.this, SecondActivity.class))。
编译与安装(推荐:在仓库内用 Gradle,无需 AOSP 树):
进入 startactivitydemo 工程目录后执行:
./gradlew assembleDebugadb install -r app/build/outputs/apk/debug/app-debug.apk
(若目录内暂无 gradlew,可先执行 gradle wrapper 或使用 Android Studio 打开 startactivitydemo 工程后构建。)
操作步骤(照着做即可)
|
|
|
|
|---|---|---|
|
|
|
|
|
|
adb logcat -s ActivityTaskManager |
|
|
|
|
|
|
|
|
|
|
|
adb shell dumpsys activity activities |
SecondActivity 或 com.example.android.startactivitydemo → 目标已入栈 |
真实日志片段(步骤 4 可能看到的)
点击按钮后,logcat 中可能出现类似输出(具体行因版本/DEBUG 开关略有差异):
ActivityTaskManager: startActivity: callingPackage=com.example.android.startactivitydemo ...ActivityTaskManager: ... (ActivityStarter / startActivityAsUser 等相关行)
若开启 DEBUG_ACTIVITY_STARTS,会有更细的 start 日志。
真实 dumpsys 片段(步骤 5 可能看到的)
在 dumpsys activity activities 输出中搜索 SecondActivity,可能看到类似:
* Task id 123 * ActivityRecord{... com.example.android.startactivitydemo/.SecondActivity ...}
说明该 Activity 已在栈中,可继续用 top-resumed / visible 看是否已是前台。
常用命令(复现时直接用)
adb logcat -s ActivityTaskManageradb logcat | grep -E "ActivityTaskManager|ActivityStarter|ActivityStartController"adb shell dumpsys activity activitiesadb shell dumpsys activity top-resumedadb shell dumpsys activity visible
六、方法速查(仅本篇涉及)
上面堆栈涉及的主要类与方法,下表速查。
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
七、这套方法能复用在哪里
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
八、下一步可以怎么做
- 继续排查
:若已确认请求进 ATMS、目标已入栈,但界面仍不出现,可看 第 2 篇:AMS/ATMS 如何决定能不能启动:该篇包含「能不能启动」的决策主干,以及决策通过后 ClientLifecycleManager.scheduleTransaction 的触发时机;再结合 dumpsys activity activities看目标 Activity 状态。 - 扩展观察
:用 PendingIntent.send 触发启动,抓 log 对比:应走 ATMS.startActivityIntentSender,不经过当前进程的Instrumentation.execStartActivity。 -
若你遇到「startActivity 没报错但界面不出」的同类问题,可按本文步骤先确认「进 ATMS + 入栈」再往后续链路上追;有具体日志/场景也可以按项目规范提 issue 或交流。
Android 14→15→16:这个环节变化点
- 决策点变更
:startActivity 入口仍为 Instrumentation → ATMS;PendingIntent 仍走 ATMS.startActivityIntentSender。若后续版本将「启动校验」前移到其他服务(如 PermissionController),以该版本 release note 为准。 - 关键类/接口变更
:ActivityTaskManagerService、ActivityStarter、IActivityTaskManager 在本环节为主入口,类名未变;若 AIDL 增删方法,需对照 IActivityTaskManager.aidl。 - 观测点变更
: dumpsys activity activities、dumpsys activity intents输出格式可能随版本微调;log tag 仍以 ActivityTaskManager、ActivityStarter 为主。新版本若有启动 trace tag,以 systrace/perfetto 文档为准。
本文由 Jinno2025 原创,转载请注明出处。
夜雨聆风
