当前位置: 首页 > news >正文

Android App启动流程深度解析(一):从Activity.startActivity开始

前言

在Android应用开发中,启动流程是最基础也是最重要的机制之一。作为一名Android开发者,理解应用启动的完整流程对于性能优化、问题排查和架构设计都至关重要。本系列文章将从最上层的Activity.startActivity()方法开始,自上而下深入分析Android应用启动的完整流程。

版本说明

本文分析的代码基于Android 10(API 29)及以上版本。在Android 10中,Google对Activity管理架构进行了重大调整:

  • Android 9及之前:所有Activity相关功能由ActivityManagerService(AMS)统一管理
  • Android 10及之后:将Activity和Task管理功能拆分到新的ActivityTaskManagerService(ATMS),AMS保留其他核心功能

这个变更有以下背景:

  1. 解耦Activity管理与系统其他核心功能
  2. 为多窗口、自由形态窗口等新特性提供更好的支持
  3. 改善系统服务的模块化程度

从Activity.startActivity开始

让我们从最常见的场景出发——一个Activity启动另一个Activity。开发者最熟悉的调用方式就是startActivity()方法。

Activity.startActivity()

@Override
public void startActivity(Intent intent) {this.startActivity(intent, null);
}

这是Activity类中最简单的startActivity重载方法。可以看到它调用了另一个重载方法,并传入了一个null的Bundle参数。

Activity.startActivity(Intent, Bundle)

public void startActivity(Intent intent, @Nullable Bundle options) {if (options != null) {startActivityForResult(intent, -1, options);} else {startActivityForResult(intent, -1);}
}

这个方法检查是否有附加的options Bundle,然后调用startActivityForResult()方法。注意这里传入的requestCode是-1,表示我们不需要返回结果。

Activity.startActivityForResult()

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,@Nullable Bundle options) {// 检查是否有父Activityif (mParent == null) {options = transferSpringboardActivityOptions(options);// 关键调用Instrumentation.ActivityResult ar =mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options);if (ar != null) {mMainThread.sendActivityResult(mToken, mEmbeddedID, requestCode, ar.getResultCode(),ar.getResultData());}if (requestCode >= 0) {mStartedActivity = true;}cancelInputsAndStartExitTransition(options);} else {// 如果有父Activity,则通过父Activity启动if (options != null) {mParent.startActivityFromChild(this, intent, requestCode, options);} else {mParent.startActivityFromChild(this, intent, requestCode);}}
}

这个方法有几个关键点:

  1. 首先检查是否有父Activity,如果没有则继续流程,否则通过父Activity启动
  2. 使用InstrumentationexecStartActivity方法执行实际的启动操作
  3. 如果有返回结果,则通过ActivityThread发送结果
  4. 处理Activity过渡动画

Instrumentation.execStartActivity()

public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {// 获取IApplicationThread对象IApplicationThread whoThread = (IApplicationThread) contextThread;// 记录来源Activityif (mActivityMonitors != null) {synchronized (mSync) {final int N = mActivityMonitors.size();for (int i=0; i<N; i++) {final ActivityMonitor am = mActivityMonitors.get(i);if (am.match(who, null, intent)) {am.mHits++;if (am.isBlocking()) {return requestCode >= 0 ? am.getResult() : null;}break;}}}}try {intent.migrateExtraStreamToClipData();intent.prepareToLeaveProcess(who);// 关键调用:通过ActivityTaskManager启动Activityint result = ActivityTaskManager.getService().startActivity(whoThread, who.getBasePackageName(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()),token, target != null ? target.mEmbeddedID : null,requestCode, 0, null, options);// 检查启动结果checkStartActivityResult(result, intent);} catch (RemoteException e) {throw new RuntimeException("Failure from system", e);}return null;
}

这个方法做了以下几件事:

  1. 处理ActivityMonitor(用于测试)
  2. 准备Intent数据
  3. 通过ActivityTaskManager.getService()获取AMS的代理对象,调用startActivity方法
  4. 检查启动结果

ActivityTaskManager.getService()

public static IActivityTaskManager getService() {return IActivityTaskManagerSingleton.get();
}private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =new Singleton<IActivityTaskManager>() {@Overrideprotected IActivityTaskManager create() {// 获取ActivityTaskManagerService的代理对象final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);return IActivityTaskManager.Stub.asInterface(b);}};

这里通过Binder机制获取了系统服务ActivityTaskManagerService的代理对象。ATMS是Android 10之后从AMS中拆分出来的专门管理Activity和Task的服务。

checkStartActivityResult()

public static void checkStartActivityResult(int res, Object intent) {switch (res) {case ActivityManager.START_INTENT_NOT_RESOLVED:case ActivityManager.START_CLASS_NOT_FOUND:throw new ActivityNotFoundException("Unable to find explicit activity class "+ ((Intent)intent).getComponent().toShortString()+ "; have you declared this activity in your AndroidManifest.xml?");case ActivityManager.START_PERMISSION_DENIED:throw new SecurityException("Not allowed to start activity "+ ((Intent)intent).getComponent().toShortString());// 其他错误处理...}
}

这个方法将ATMS返回的结果转换为相应的异常,开发者常见的"Activity未在Manifest中声明"的错误就是在这里抛出的。

流程概览

到目前为止,我们看到的调用流程如下:

  1. Activity.startActivity()
  2. Activity.startActivity(Intent, Bundle)
  3. Activity.startActivityForResult()
  4. Instrumentation.execStartActivity()
  5. ActivityTaskManager.getService().startActivity()

这个流程已经从应用进程进入了系统进程(system_server),后续的处理将在ATMS中进行。

为什么需要Instrumentation?

你可能注意到在流程中有一个Instrumentation类参与其中。Instrumentation是Android测试框架的核心部分,但它也参与正常的Activity启动流程。它的主要作用有:

  1. 提供一种监控系统与应用交互的机制
  2. 在测试中可以替换组件行为
  3. 在正常运行时作为Activity生命周期回调的中间层

小结

本文从最常用的Activity.startActivity()方法开始,逐步深入分析了Android应用启动的前半段流程。我们了解到:

  1. 启动请求最终通过Instrumentation转发到系统服务
  2. 通过Binder机制与系统服务ActivityTaskManagerService交互
  3. 基本的错误检查和处理机制

附:关键差异对比表

组件/版本Android 9及之前Android 10及之后
核心服务ActivityManagerService (AMS)ActivityTaskManagerService (ATMS)
获取服务方式ActivityManager.getService()ActivityTaskManager.getService()
管理范围包含Activity管理在内的所有功能专注Activity和Task管理
Binder接口IActivityManagerIActivityTaskManager

兼容性说明

虽然架构发生了变化,但上层API保持了兼容:

// 这两种写法在Android 10+上效果相同,但后者是推荐的新方式
startActivity(intent); // 仍然可用
ActivityTaskManager.getService().startActivity(...); // 新方式

系统通过兼容层处理了API差异,因此开发者无需修改现有代码。

为什么需要拆分ATMS?

在Android 10的架构变更中,拆分ATMS的主要考虑包括:

  1. 职责单一化

    • AMS原本承担了太多职责(内存管理、进程管理、Activity管理等)
    • 拆分后ATMS专注于Activity和Task管理
  2. 多窗口支持

    • 新的多窗口模式需要更复杂的Activity栈管理
    • 独立服务可以更灵活地实现这些功能
  3. 性能优化

    • 减少AMS的锁竞争
    • 并行处理Activity相关操作

代码路径差异

在分析启动流程时,需要注意不同版本的代码路径差异:

Android 9及之前

// 老版本通过AMS启动Activity
ActivityManager.getService().startActivity(...)

Android 10及之后

// 新版本通过ATMS启动Activity
ActivityTaskManager.getService().startActivity(...)
http://www.lryc.cn/news/571127.html

相关文章:

  • 常用交易所链接
  • 【PmHub面试篇】PmHub分布式事务Seata保证任务审批状态一致性面试专题解析
  • fd查找文件并拷贝
  • 【启发式算法】RRT算法详细介绍(Python)
  • OSPF 配置全攻略:从基础原理到实战演练
  • python 重要地位
  • UE/Unity/Webgl云渲染推流网址,如何与外部网页嵌套和交互?
  • 答题考试系统小程序ThinkPHP+UniApp
  • NLP学习路线图(五十一):PyTorch/TensorFlow
  • 用C语言实现面向对象思想编写驱动层--以驱动LED为例
  • R情感分析:解码文本中的情感
  • 《从IaaS到容器化:深度解析云计算三层架构与阿里云ECS+K8s协同实践》
  • Web攻防-XSS跨站文件类型功能逻辑SVGPDFSWFHTMLXMLPMessageLocalStorage
  • Jeston orin nano利用SDK Manager安装ubuntu系统
  • 【Mac 上离线安装 ADB 工具】
  • iOS即时通讯发送图片消息内存暴涨优化
  • OpenCV CUDA模块设备层-----用于CUDA 纹理内存(Texture Memory)的封装类cv::cudev::Texture
  • 《计算机网络·自顶向下方法》第 2 章 应用层
  • 六.架构设计之存储高性能——缓存
  • K8S 专栏 —— namespace和Label篇
  • Gödel Rescheduler:适用于云原生系统的全局最优重调度框架
  • 实现汽车焊装线设备互联:DeviceNet与Modbus TCP协议转换网关
  • 10.C S编程错误分析
  • 数字电路研究的是直流信号还是交流信号
  • 远程桌面连接 - 允许电脑从网络外部访问计算机
  • 当 GitLab 服务器网络配置发生变化,如何修改
  • 【Unity笔记】Unity URP 渲染中的灯光数量设置— 场景、使用方法与渲染原理详解
  • 黑龙江亿林数据 - 服务器托管
  • 无人机数据处理系统设计与难点
  • Uniapp插件改造指南:如何让vue-plugin支持HarmonyOS5原生能力?