Instrumentation
Instrumentation 是什么?
Instrumentation 是 App 进程内部的生命周期调度器。它创建 Application、创建 Activity、执行全部生命周期。ActivityThread 发命令,Instrumentation 真正干活。插件化、热修复、测试框架 都靠它。
Instrumentation 是 Android 系统用来“监控、调度、创建、调用” Activity、Application、Service 生命周期的调度器 + 钩子类。
它介于 AMS ↔ App 进程 之间,所有生命周期方法都必须经过它,才能真正执行。
- 它是一个 普通 Java 类,位于
android.app.Instrumentation - 每个 App 进程 有且仅有一个实例,由 ActivityThread 创建持有
- 它不是四大组件,不是服务,不是 binder
- 它是 系统留给 App 的“生命周期钩子”
- 同时也是 单元测试、插件化 的核心基础
你可以把它理解为: Activity/Application/Service 的“总调度员” 所有生命周期方法,都必须由它来“下令执行”。
核心作用(必须记住)
1. 创建 Application
instrumentation.newApplication(...)
instrumentation.callApplicationOnCreate(...)
Application 不由系统直接创建,必须走 Instrumentation。
2. 创建 Activity 实例
instrumentation.newActivity(...)
通过类加载器反射创建 Activity。
3. 执行 Activity 全部生命周期
callActivityOnCreate()
callActivityOnStart()
callActivityOnResume()
callActivityOnPause()
callActivityOnStop()
callActivityOnDestroy()
没有任何生命周期能绕过它。
4. 分发按键、触摸事件
callActivityOnKeyDown()
callActivityOnTouchEvent()
5. 提供系统调用的“钩子”
插件化、热修复、黑科技 90% 靠 hook Instrumentation 实现。
6. 支撑 instrumentation 测试
JUnit、Espresso 测试都依赖它来控制 Activity。
它在启动流程中的位置(超级重要)
AMS
↓(Binder)
ApplicationThread(Binder线程)
↓(Handler 切主线程)
ActivityThread
↓
Instrumentation ←←← 核心在这里
↓
Activity/Application/Service
ActivityThread 只发指令,Instrumentation 真正执行生命周期。
关键方法(面试高频)
Application 相关
newApplication(cl, className, context)创建 ApplicationcallApplicationOnCreate(app)调用Application.onCreate()
Activity 相关
newActivity(cl, className, intent)创建 ActivitycallActivityOnCreate()callActivityOnStart()callActivityOnResume()callActivityOnPause()callActivityOnStop()callActivityOnDestroy()
事件相关
newActivity()创建ActivitycallActivityOnKeyDown/UpcallActivityOnTouchEvent
启动Activity
execStartActivity()系统启动Activity的真正入口,Hook 高发点
为什么系统要设计 Instrumentation?
1. 统一生命周期调度
避免 ActivityThread 代码过于臃肿。
2. 提供“钩子”能力
系统允许开发者替换 Instrumentation,实现:
- 插件化(占坑 Activity)
- 热修复
- 无埋点统计
- 行为监控
3. 支持自动化测试
测试框架可以接管 Activity 的创建与生命周期。
4. 安全与隔离
系统不直接调用 App 组件,通过 Instrumentation 间接调用,更安全。
一句话理解它的工作模式
系统不直接调用 Activity 的 onCreate, 而是让 Instrumentation 去调用 Activity 的 onCreate。
系统 → Instrumentation → Activity.onCreate()
常考面试题(标准答案)
Q1:Instrumentation 是什么?
AMS 调度 App 生命周期的中间调度器,负责创建、执行 Activity/Application 的所有生命周期。
Q2:Activity 的 onCreate 是谁调用的?
最终由 Instrumentation.callActivityOnCreate() 调用。
Q3:ActivityThread 和 Instrumentation 关系?
ActivityThread 持有 Instrumentation 实例,生命周期逻辑由它执行。
Q4:插件化为什么要 Hook Instrumentation?
因为所有 Activity 都由它创建,Hook 它可以替换要启动的 Class,实现插件 Activity。
Q5:Instrumentation 运行在哪个进程?
App 进程主线程,不是 system_server。
Instrumentation 插件化
Hook 原理 + 源码 + 代码实现
为什么插件化必须 Hook Instrumentation?怎么 Hook?
为什么要 Hook Instrumentation?
因为:Activity 启动必须经过 2 道关卡
第 1 关:AMS 校验(系统进程)
AMS 会检查要启动的 Activity 是否在 AndroidManifest.xml 里注册过。 没注册 → 直接崩溃:
ActivityNotFoundException
第 2 关:App 进程创建实例
AMS 校验通过后,回到 App 进程,Instrumentation 创建 Activity
插件化的痛点
插件里的 Activity 没有在宿主 manifest 注册, 过不了 AMS 校验。
解决方案(插件化核心思想):偷梁换柱
- 启动插件 Activity 时,先替换成一个已注册的“占坑 Activity”
- 骗过 AMS 校验
- AMS 校验通过后,再替换回插件 Activity
- 让 Instrumentation 真正创建插件 Activity
这两步替换,都必须通过 Hook 实现,而 Hook 点就是:Instrumentation
源码级:Activity 启动流程(核心 3 段)
1. 启动入口:Activity.startActivity()
最终会走到:Instrumentation.execStartActivity()
2. AMS 校验
在这里检查 AndroidManifest
3. 回到 App 进程
Instrumentation.newActivity()
创建 Activity 实例
Hook 原理图(一句话)
Hook Instrumentation → 拦截 execStartActivity → 替换 Intent → 骗过 AMS → 拦截 newActivity → 还原插件 Activity
完整可运行 Hook 代码(最标准实现)
第一步:自定义 Instrumentation
class HookInstrumentation(private val base: Instrumentation) : Instrumentation() {
// 1. 拦截启动Activity,替换为占坑Activity
override fun execStartActivity(
who: Context?,
contextThread: IBinder?,
token: IBinder?,
target: Activity?,
intent: Intent?,
requestCode: Int,
options: Bundle?
): ActivityResult {
// 判断是否是插件Activity
if (intent?.component?.className?.contains("plugin") == true) {
// 保存插件真正的Activity
val pluginIntent = Intent(intent)
// 替换成已注册的占坑Activity
intent?.setClass(who!!, StubActivity::class.java)
}
// 调用原来的方法
val execStartActivity = Instrumentation::class.java.getDeclaredMethod(
"execStartActivity",
Context::class.java,
IBinder::class.java,
IBinder::class.java,
Activity::class.java,
Intent::class.java,
Int::class.java,
Bundle::class.java
)
return execStartActivity.invoke(base, who, contextThread, token, target, intent, requestCode, options) as ActivityResult
}
// 2. 拦截创建Activity,还原插件Activity
override fun newActivity(cl: ClassLoader?, className: String?, intent: Intent?): Activity {
// 如果是占坑Activity,还原插件
if (intent?.component?.className == StubActivity::class.java.name) {
val pluginClassName = intent.getStringExtra("plugin_class")
return super.newActivity(cl, pluginClassName, intent)
}
return super.newActivity(cl, className, intent)
}
}
第二步:Hook 替换系统的 Instrumentation
fun hookInstrumentation() {
// 1. 获取 ActivityThread
val activityThread = Class.forName("android.app.ActivityThread")
.getDeclaredMethod("currentActivityThread")
.invoke(null)
// 2. 获取原来的 Instrumentation
val mInstrumentationField = Class.forName("android.app.ActivityThread")
.getDeclaredField("mInstrumentation")
mInstrumentationField.isAccessible = true
val baseInstrumentation = mInstrumentationField.get(activityThread) as Instrumentation
// 3. 创建自己的 HookInstrumentation
val hookInstrumentation = HookInstrumentation(baseInstrumentation)
// 4. 替换
mInstrumentationField.set(activityThread, hookInstrumentation)
}
第三步:在 Application 中启动 Hook
class App : Application() {
override fun onCreate() {
super.onCreate()
hookInstrumentation()
}
}
第四步:占坑 Activity(必须在 manifest 注册)
class StubActivity : AppCompatActivity()
<activity android:name=".StubActivity"/>
启动插件 Activity
val intent = Intent()
intent.setClassName("com.plugin", "com.plugin.PluginActivity")
intent.putExtra("plugin_class", "com.plugin.PluginActivity")
startActivity(intent)
整个流程(超级清晰)
- 启动插件 Activity
- Hook #1:execStartActivity 替换成 StubActivity
- AMS 校验 StubActivity(已注册 → 校验通过)
- AMS 批准启动
- Hook #2:newActivity 还原成插件 Activity
- 插件 Activity 正常显示
面试标准答案(背这个)
Q:插件化为什么要 Hook Instrumentation?
因为 Activity 的启动和创建都由 Instrumentation 控制。Hook 它可以在 AMS 校验前替换成占坑 Activity,校验通过后再还原成插件 Activity,从而绕过 manifest 注册检查。
Q:Hook 了哪两个方法?
- execStartActivity() → 替换 Intent 骗过 AMS
- newActivity() → 还原插件 Activity
Q:Instrumentation 作用?
创建 Activity、执行生命周期、启动 Activity、分发事件,是系统与 App 组件之间的调度器。
如何在 Android 中使用 Hook 技术?
先搞懂:Hook 到底是什么?
一句话
Hook = 拦截系统方法 → 在方法执行前后插入自己的代码 → 改变原方法的行为。
你可以把它理解为:给系统API“加一层拦截器”。
核心原理
Java 是反射 Kotlin 也是反射 通过反射拿到系统类的私有成员变量 / 方法,替换成我们自己的对象。
Android Hook 能做什么?(超级实用)
- 插件化(启动未注册的 Activity)
- 热修复(替换错误方法)
- 无埋点统计(自动统计点击、页面)
- 拦截点击/生命周期/网络
- 防止崩溃(捕获系统异常)
- 换肤、权限拦截、全局监控
Android Hook 分为 2 种
1. 应用层 Hook(最常用)
只 Hook 本应用进程 使用 Java 反射 + 动态代理 代表:Hook Instrumentation、Hook OnClickListener、Hook AMS
2. 系统级 Hook(需要 root)
Hook 整个手机系统 使用 Xposed、Frida 代表:抢红包、改定位
最核心:Hook 的 3 步万能公式(背会就会写)
万能 Hook 三步法
- 找到要 Hook 的目标对象(系统私有对象,如 Instrumentation)
- 通过反射拿到它
- 替换成我们自己的代理对象
目标对象 → 反射获取 → 替换成自定义对象 → Hook 成功
实战 1:最简单 Hook —— 拦截按钮点击
目标
Hook 所有setOnClickListener,在点击前输出日志。
原理
View 的点击事件最终交给ListenerInfo对象 我们 Hook 它的mOnClickListener
完整可运行代码
fun hookOnClickListener(view: View) {
try {
// 1. 获取 View 的 ListenerInfo
val getListenerInfo = View::class.java.getDeclaredMethod("getListenerInfo")
getListenerInfo.isAccessible = true
val listenerInfo = getListenerInfo.invoke(view)
// 2. 获取 ListenerInfo 里的 mOnClickListener
val mOnClickListenerField = Class.forName("android.view.View\$ListenerInfo")
.getDeclaredField("mOnClickListener")
mOnClickListenerField.isAccessible = true
// 3. 获取原来的点击事件
val originalListener = mOnClickListenerField.get(listenerInfo) as View.OnClickListener
// 4. 动态代理替换
val proxyListener = Proxy.newProxyInstance(
View.OnClickListener::class.java.classLoader,
arrayOf(View.OnClickListener::class.java)
) { _, method, args ->
// ↓↓↓ 这里就是 Hook 逻辑!拦截点击
Log.d("Hook", "按钮被点击了!")
// 执行原点击事件
method.invoke(originalListener, *args.orEmpty())
}
// 5. 替换
mOnClickListenerField.set(listenerInfo, proxyListener)
} catch (e: Exception) {
e.printStackTrace()
}
}
使用
btn.setOnClickListener {
Log.d("Hook","原点击")
}
hookOnClickListener(btn)
结果
点击按钮 → 先输出 Hook 日志 → 再执行原点击 拦截成功!
实战 2:最经典 Hook —— Hook Instrumentation(插件化核心)
目标
拦截所有 Activity 启动,输出日志。
完整代码
fun hookInstrumentation() {
try {
// 1. 获取 ActivityThread
val activityThreadCls = Class.forName("android.app.ActivityThread")
val currentActivityThreadMethod = activityThreadCls.getDeclaredMethod("currentActivityThread")
val activityThread = currentActivityThreadMethod.invoke(null)
// 2. 获取 mInstrumentation 字段
val mInstrumentationField = activityThreadCls.getDeclaredField("mInstrumentation")
mInstrumentationField.isAccessible = true
val originalInstrumentation = mInstrumentationField.get(activityThread) as Instrumentation
// 3. 创建自己的代理
val proxyInstrumentation = object : Instrumentation() {
override fun callActivityOnCreate(activity: Activity?, icicle: Bundle?) {
Log.d("Hook", "Activity 创建: ${activity?.javaClass?.simpleName}")
originalInstrumentation.callActivityOnCreate(activity, icicle)
}
override fun newActivity(cl: ClassLoader?, className: String?, intent: Intent?): Activity {
Log.d("Hook", "创建Activity: $className")
return originalInstrumentation.newActivity(cl, className, intent)
}
}
// 4. 替换
mInstrumentationField.set(activityThread, proxyInstrumentation)
} catch (e: Exception) {
e.printStackTrace()
}
}
在 Application 中启动 Hook
class App : Application() {
override onCreate() {
super.onCreate()
hookInstrumentation()
}
}
效果
所有Activity创建都会被拦截打印日志
实战 3:Hook AMS 启动 Activity(插件化占坑核心)
目标
启动未在 AndroidManifest 注册的 Activity。
原理
- Hook IActivityManager
- 启动前替换 Intent 为占坑 Activity
- 骗过 AMS
- 再还原回来
代码(简化版)
fun hookAMS() {
try {
val defaultCls = Class.forName("android.app.ActivityManager\$IActivityManagerSingleton")
val sInstance = defaultCls.getDeclaredField("sInstance")
sInstance.isAccessible = true
val ams = sInstance.get(null)
val proxy = Proxy.newProxyInstance(
Thread.currentThread().contextClassLoader,
arrayOf(Class.forName("android.app.IActivityManager"))
) { _, method, args ->
if (method.name == "startActivity") {
// 在这里替换 Intent → 占坑
val intent = args[2] as Intent
val targetIntent = Intent(intent)
val stubIntent = Intent()
stubIntent.setClassName("com.demo", "com.demo.StubActivity")
args[2] = stubIntent
}
method.invoke(ams, *args.orEmpty())
}
sInstance.set(null, proxy)
} catch (e: Exception) {
e.printStackTrace()
}
}
Hook 技术总结(最核心)
1. Hook 本质
反射 + 动态代理 替换系统对象
2. 万能步骤
- 找目标对象
- 反射获取
- 动态代理替换
- 插入逻辑
3. 常用 Hook 点
- Instrumentation(Activity 生命周期)
- IActivityManager(AMS)
- IApplicationThread(系统回调)
- View.OnClickListener(点击)
- AMS、PMS、WMS
4. 最常用场景
- 插件化
- 热修复
- 无埋点统计
- 防崩溃
- 全局监控
其它Hook应用
1. Xposed 完整教程
2. 插件化全套原理 + 占坑 + 源码
3. Hook AMS 完整可运行 Demo
4. Hook 网络请求
5. Android 系统启动全套流程图
动态代理替换是什么?
先一句话说死
动态代理 = 给原对象套一层“代理壳”,不修改原代码,就能拦截、修改、增强原方法。
替换 = 把系统原来的对象,偷偷换成我们的代理对象。
合起来:
动态代理替换 = 给系统对象套壳 → 拦截系统方法 → 实现 Hook
最通俗的比喻
你要给**领导(系统对象)**传话:
- 原来流程: 你 → 领导
- 代理后流程: 你 → 秘书(代理对象) → 领导
秘书 = 动态代理 秘书可以:
- 偷偷改话
- 加话
- 减话
- 不让传
- 记录日志
领导完全不知道,你也完全不知道。
这就是动态代理替换。
动态代理到底长什么样?(看一眼就懂)
MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
classLoader,
new Class[]{MyInterface.class},
invocationHandler
);
它做了一件事: 自动生成一个类,实现你指定的接口,然后把所有方法都交给你处理。
你就可以:
- 方法执行前干点啥
- 方法执行后干点啥
- 甚至不让原方法执行
动态代理替换的核心 3 要素
- 原对象(系统对象,如 OnClickListener、AMS、Instrumentation)
- 代理对象(你生成的壳)
- 替换动作(把系统原来的对象换成你的代理)
最经典例子:拦截按钮点击
正常点击
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("Hook","我被点击了");
}
});
动态代理替换后
你点击 → 代理拦截 → 打印日志 → 再执行原点击
代码(最精简版)
val proxyListener = Proxy.newProxyInstance(
View.OnClickListener::class.java.classLoader,
arrayOf(View.OnClickListener::class.java)
) { _, method, args ->
Log.d("Hook", "我被拦截了!")
method.invoke(originalListener, args) // 执行原点击
} as View.OnClickListener
// 替换!
field.set(listenerInfo, proxyListener)
动态代理替换的核心作用(Hook 必用)
1. 不修改源码,增强系统方法
2. 完全透明,系统不知道被替换
3. 能拦截、修改、返回、记录任何方法
4. 是 Android 插件化、热修复、Hook 的基石
为什么 Hook 必须用动态代理?
因为:
- 系统对象都是接口(AMS、Service、Binder 都是接口)
- 动态代理只能代理接口
- 所以 Hook 系统方法必须用动态代理
终极总结(背这一段就够)
动态代理 = 自动生成接口实现类,用来拦截方法
替换 = 把系统对象换成代理对象
动态代理替换 = Hook 的核心技术
Instrumentation 和 ActivityThread ApplicationThread 关系
- ActivityThread:App 进程的主线程管家,掌管整个 App 的运行。
- ApplicationThread:ActivityThread 的Binder 内部类,专门负责和 AMS 通信(接收系统指令)。
- Instrumentation:ActivityThread 的工具人/执行器,真正创建、调用 Activity/Application 的生命周期。
三者身份定位(超级形象)
把 App 进程 比作 一家公司:
- ActivityThread = 总经理
- 掌管公司一切(主线程、消息队列、四大组件)
- 不亲自干活,只发号施令
- 持有:Application、mInstrumentation、mH(Handler)
- ApplicationThread = 对外联络员(Binder)
- 总经理的专属秘书
- 只干一件事:对接 AMS(系统)
- 接收系统指令(启动Activity、暂停App)
- 运行在 Binder 线程池
- Instrumentation = 执行主管
- 总经理手下真正干活的人
- 负责:创建Activity、执行生命周期、调用onCreate
- 所有组件生命周期必须经过它
execStartActivity
一、execStartActivity 核心定位
execStartActivity 是 Android 应用进程发起 Activity 启动的「系统入口」,位于 Instrumentation 类中,是所有 startActivity/startActivityForResult 的最终统一调用点,负责把应用层的启动请求,通过 Binder 跨进程发送到系统服务(ATMS/AMS),完成权限校验、栈管理、进程调度等系统级逻辑。
一句话总结:应用层 startActivity → Instrumentation.execStartActivity → 跨进程调用 ATMS → 系统调度启动 → 应用进程回调生命周期。
二、完整源码定义(Android 10+,ATMS 架构)
// android.app.Instrumentation.java
public ActivityResult execStartActivity(
Context who,
IBinder contextThread, // 调用方进程的 ApplicationThread Binder
IBinder token, // 调用方 Activity 的 token(标识来源)
Activity target, // 目标 Activity(可为 null)
Intent intent, // 启动意图
int requestCode, // 请求码(-1 表示无返回)
Bundle options // 启动选项(动画、flags 等)
) throws RemoteException {
// 1. 安全与参数校验
IApplicationThread whoThread = (IApplicationThread) contextThread;
if (mActivityMonitors != null) {
// 遍历 ActivityMonitor,用于调试/拦截(如单元测试)
synchronized (mSync) {
final int N = mActivityMonitors.size();
for (int i=0; i<N; i++) {
final ActivityMonitor am = mActivityMonitors.get(i);
ActivityResult result = am.match(who, null, intent);
if (result != null) {
return result; // 被拦截,直接返回结果
}
}
}
}
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
// 2. 核心:跨进程调用 ATMS.startActivity
int result = ActivityTaskManager.getService().startActivity(
whoThread, // 调用方 ApplicationThread(Binder)
who.getPackageName(), // 调用方包名
intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, // 来源 Activity token
target != null ? target.mEmbeddedID : null,
requestCode,
0, // flags(如 Intent.FLAG_ACTIVITY_NEW_TASK)
options
);
// 3. 检查启动结果(抛出安全异常、权限异常等)
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw e;
}
return null;
}
关键参数解析
- who:发起启动的 Context(Activity/Application)
- contextThread:应用进程的
ApplicationThreadBinder 代理,用于系统回调应用 - token:调用方 Activity 的 IBinder token,ATMS 用它识别栈归属、生命周期管理
- intent:核心启动信息(目标组件、action、data、flags、extra)
- requestCode:≥0 时走 startActivityForResult,-1 时普通启动
- options:启动配置(过渡动画、启动模式、屏幕方向等)
三、完整调用链路(从应用到系统再回应用)
1. 应用层入口(Activity.startActivity)
所有应用层启动最终走到 Activity.startActivityForResult:
// Activity.java
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
if (mParent == null) {
// 核心:调用 Instrumentation.execStartActivity
Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(
this,
mMainThread.getApplicationThread(),
mToken,
this,
intent,
requestCode,
options
);
// 处理返回结果...
} else {
mParent.startActivityFromChild(...);
}
}
2. execStartActivity 核心工作(应用→系统)
- 拦截与监控:遍历
ActivityMonitor,支持测试框架拦截启动(如 Espresso) - Intent 预处理:
prepareToLeaveProcess清理进程内临时数据、序列化准备 - 跨进程调用:获取
IActivityTaskManager(ATMS Binder 代理),调用startActivity - 结果校验:
checkStartActivityResult抛出启动失败异常(如 ActivityNotFoundException、SecurityException)
3. 系统侧处理(ATMS/ActivityStarter)
execStartActivity 发出请求后,进入 system_server 进程:
- ATMS.startActivity → 权限/包名/Intent 合法性校验
- ActivityStarter.execute → 核心逻辑:
- 计算启动模式(standard/singleTop/singleTask/singleInstance)
- 管理 Task/ActivityStack,决定是否新建栈、复用 Activity、清理栈
- 检查目标进程是否存在:不存在则请求 Zygote fork 新进程
- 生成
ActivityRecord,记录 Activity 状态、栈信息、生命周期
- 进程调度:目标进程存在 → 发送
scheduleLaunchActivity事务;不存在 → 先 fork 进程,再启动
4. 应用进程回调(系统→应用)
- ATMS 通过
ApplicationThread.scheduleLaunchActivity发送启动事务 ActivityThread.H主线程 Handler 接收消息,执行handleLaunchActivityperformLaunchActivity:反射创建 Activity 实例 →attach绑定 Context/Window → 调用onCreate/onStart/onResume
四、版本差异(Android 9 vs 10+)
- Android 9 及更早:调用
ActivityManager.getService()(AMS),IActivityManager - Android 10+:拆分出 ATMS(ActivityTaskManagerService),专注 Activity/Task 栈管理,调用
ActivityTaskManager.getService(),IActivityTaskManager
五、核心作用与面试考点
1. 核心作用
- 统一入口:所有应用启动 Activity 必经此处,是应用与系统的分界点
- 安全校验:拦截非法启动、权限检查、Intent 合法性
- 跨进程桥梁:封装 Binder 调用,屏蔽应用层与 system_server 通信细节
- 调试/测试入口:
ActivityMonitor用于单元测试、启动拦截
2. 高频面试题
execStartActivity 在哪里?谁调用它?
- 位置:
android.app.Instrumentation类中。 - 调用者:Activity、ActivityTaskManager、Application 等发起 startActivity 时,最终都会走到
Activity.startActivityForResult()→ 内部调用mInstrumentation.execStartActivity()。 - 它是应用进程内部发起 Activity 启动的统一系统入口。
execStartActivity 的作用是什么?
- 作为应用层到系统服务的统一启动入口,所有 startActivity 都必须经过它。
- 做 Intent 预处理,让 Intent 可以安全跨进程传输。
- 通过 Binder 跨进程调用 ATMS(ActivityTaskManagerService) 的 startActivity 方法。
- 支持测试框架(ActivityMonitor)拦截启动流程。
- 校验启动结果,抛出启动失败异常(如 Activity 未注册、权限异常)。
一句话总结: execStartActivity 就是把应用的启动请求,打包发给系统服务,并检查结果。
为什么 execStartActivity 要放在 Instrumentation 里?
- Instrumentation 本身就是 Android 设计用来监控、拦截、控制应用生命周期的类。
- 把启动入口放在这里,方便:
- 做统一的启动拦截(单元测试、插件化、黑科技拦截)。
- 统一做安全校验、日志、统计。
- 解耦 Activity 与系统服务,让系统逻辑不侵入业务组件。
一句话: 为了统一管控、方便拦截与测试,符合系统架构的设计思想。
execStartActivity 如何跨进程到 ATMS/AMS?
execStartActivity 内部获取 ActivityTaskManagerService 的 Binder 代理。
调用其远程方法:
ActivityTaskManager.getService().startActivity(...)跨进程进入 system_server 进程,由 ATMS 处理栈管理、权限、进程创建。
系统处理完后,再通过 ApplicationThread 跨进程回调应用进程。
execStartActivity 和 startActivity 有什么区别?
- startActivity:应用层 API,业务调用,面向开发者。
- execStartActivity:系统内部方法,在 Instrumentation 中,是 startActivity 的真正实现入口。
- 关系:
Activity.startActivity → startActivityForResult → execStartActivity
从 startActivity 到 execStartActivity 再到界面显示的完整流程(高频)
标准答案(背这个):
- 调用
Activity.startActivity。 - 进入
startActivityForResult。 - 调用
Instrumentation.execStartActivity。 - 跨进程 Binder 调用 ATMS.startActivity。
- system_server 中:
- 权限检查
- 查找目标 Activity
- 处理启动模式、任务栈
- 目标进程不存在则 fork 进程
- ATMS 通过
ApplicationThread跨进程发送启动事务。 - 应用主线程 Handler 处理
handleLaunchActivity。 performLaunchActivity反射创建 Activity,执行 onCreate/onStart/onResume。- WMS 添加窗口,界面显示。
execStartActivity 会抛出哪些常见异常?
ActivityNotFoundException:未注册或找不到目标 Activity。SecurityException:权限不足、非法跨应用启动。RemoteException:system_server 崩溃或 Binder 死亡。IllegalStateException:生命周期异常、Context 异常。
这些异常都来自 checkStartActivityResult(result, intent)。
插件化为什么要 hook execStartActivity?
因为 Android 系统只允许启动在 AndroidManifest 中注册的 Activity。 插件 Activity 没注册,系统会直接报错。
插件化框架通过 hook Instrumentation 中的 execStartActivity:
- 拦截启动请求。
- 将插件 Activity 替换为一个已注册的“占坑 Activity”。
- 骗过系统校验。
- 后续再还原回真正的插件 Activity。
所以 execStartActivity 是插件化的核心 Hook 点。
execStartActivity 中的 token 是什么?有什么用?
token 是目标 Activity 对应的 IBinder 对象,由系统创建。 作用:
- ATMS 用它标识 Activity 身份。
- 管理任务栈、生命周期、窗口绑定。
- 防止非法伪造启动 Activity。
六、常见异常与排查
- ActivityNotFoundException:Intent 目标组件不存在、未在 AndroidManifest 注册
- SecurityException:权限不足、跨应用启动未声明权限、Intent 不安全
- RemoteException:ATMS 进程崩溃、Binder 通信失败
- 排查:先看
checkStartActivityResult抛出的具体 result 码,再核对清单、权限、Intent 配置
需要我把 execStartActivity 到 ATMS 再到 Activity 生命周期的完整流程,整理成一页可直接背诵的面试速记版时序图+关键代码片段吗?