4、Activity 启动流程(上):从startActivity到AMS的IPC调用链路
各位同学,今天我们来啃一块硬骨头——Activity的启动流程。说实话,这块内容我当年刚接触时也觉得很绕,代码调用链又长又深。但别怕,我们把它拆成上下两讲,今天先聚焦前半段:从你调用startActivity()开始,到消息最终抵达AMS(Activity Manager Service)的完整IPC链路。
嗯,说白了,就是搞清楚「你的App进程」和「系统进程(System Server)」之间,到底是怎么搭上话的。
4.1 起点:startActivity() 到底干了什么?
我们平时写代码,启动一个Activity通常就一行:
startActivity(new Intent(this, TargetActivity.class));
但这一行背后,藏着好几层封装。我个人习惯把调用链分成三段来看:
- 应用层调用:Activity.startActivity() → Instrumentation.execStartActivity()
- 跨进程通信层:Instrumentation → ActivityManagerNative(实际是ActivityManagerProxy)→ Binder驱动
- 系统服务层:AMS.onStartActivity() 接收请求
你想想看,你的App跑在自己的进程里,AMS跑在System Server进程里,两个进程怎么通信?靠的就是Binder。这里我提一句,Binder是Android IPC的基石,如果你对Binder不熟,建议先补一下,不然后面看AMS的代码会有点吃力。
4.2 Instrumentation:被忽略的幕后管家
很多人以为startActivity()直接调了AMS,其实中间还夹着一个Instrumentation。这个类平时不显山不露水,但它的作用非常大。
我们来看一下源码(基于Android 13,但核心逻辑没变):
// Activity.java
public void startActivity(Intent intent) {
startActivityForResult(intent, -1);
}
public void startActivityForResult(Intent intent, int requestCode) {
// ... 省略参数检查
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
}
注意这里传了一个mMainThread.getApplicationThread(),这个参数很关键。它是ApplicationThread对象,是ActivityThread的内部类,本质上是一个Binder服务端。AMS后续要通过它来回调你的App进程。
我曾经在排查一个ANR问题时,发现就是ApplicationThread的Binder调用超时了,导致AMS等不到回调,直接杀了进程。嗯,这个坑后面细说。
4.3 进入Instrumentation.execStartActivity()
好,我们继续往下走。看看Instrumentation里做了什么:
// Instrumentation.java
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
// 1. 获取AMS的代理对象
IActivityManager manager = ActivityManager.getService();
// 2. 调用AMS的startActivity方法
int result = manager.startActivity(
whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
// 3. 检查结果
checkStartActivityResult(result, intent);
return null;
}
这里有个关键点:ActivityManager.getService()返回的是什么?
说白了,它返回的是一个Binder代理对象。这个代理对象封装了与AMS通信的所有细节。你调用manager.startActivity()时,实际上是在向Binder驱动发送一个TRANSACTION码,Binder驱动再把数据传递给System Server进程中的AMS。
核心要点:ActivityManager.getService() 返回的是 ActivityManagerProxy 对象,它实现了 IActivityManager 接口。这个接口定义了AMS对外提供的所有服务方法。
4.4 Binder调用链路:从Proxy到Stub
我们来看一下ActivityManagerProxy.startActivity()的实现:
// ActivityManagerProxy.java (已废弃,但逻辑类似)
public int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags,
ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
// 写入Binder接口描述
data.writeInterfaceToken(IActivityManager.descriptor);
// 写入参数
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
data.writeString(callingPackage);
intent.writeToParcel(data, 0);
// ... 省略其他参数写入
// 通过Binder驱动发送请求
mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
// 读取返回结果
int result = reply.readInt();
reply.recycle();
data.recycle();
return result;
}
看到mRemote.transact()这行了吗?这就是真正的跨进程调用。mRemote是BinderProxy对象,它会把数据打包后发给Binder驱动。Binder驱动再找到AMS对应的Binder实体(即ActivityManagerNative的子类),调用它的onTransact()方法。
在AMS端,接收流程是这样的:
// ActivityManagerNative.java (实际是ActivityManagerService继承它)
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
switch (code) {
case START_ACTIVITY_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
// 读取参数
IBinder b = data.readStrongBinder();
IApplicationThread caller = IApplicationThread.Stub.asInterface(b);
String callingPackage = data.readString();
Intent intent = Intent.CREATOR.createFromParcel(data);
// ... 省略其他参数读取
// 调用真正的AMS方法
int result = startActivity(caller, callingPackage, intent, ...);
reply.writeNoException();
reply.writeInt(result);
return true;
}
}
}
个人经验:我在做系统稳定性优化时,经常需要抓Binder调用日志。用adb shell dumpsys binder可以查看当前所有Binder线程的状态。如果发现某个Binder调用卡住了,基本就能定位到是哪个服务出了问题。
4.5 整体调用链路图
为了让你更直观地理解,我画了一张流程图。这张图把从App进程到AMS的完整IPC路径展示出来了:
4.6 关键参数:ApplicationThread 的作用
刚才提到,execStartActivity()里传了一个whoThread参数,它是ApplicationThread对象。这个对象为什么重要?
因为AMS在收到启动请求后,需要做很多事情:检查权限、解析Intent、创建ActivityRecord、处理栈管理……但这些都发生在系统进程。当AMS决定要真正创建Activity时,它需要回调你的App进程来执行Activity.onCreate()等生命周期方法。
这个回调,就是通过ApplicationThread完成的。AMS持有它的Binder引用,可以跨进程调用它的方法。
避坑指南:我曾经遇到一个诡异的问题——App启动时闪退,但没有任何异常日志。后来发现是ApplicationThread的Binder引用在某个时机被释放了,导致AMS回调时找不到目标。这种情况通常发生在进程被异常杀死后重建时,Binder引用没有正确恢复。
4.7 小结:这一段的本质是什么?
好了,我们来总结一下今天的内容。从startActivity()到AMS的IPC调用链路,本质上就是:
- 你的App进程:通过Binder代理对象,把启动请求打包成Parcel数据,发给Binder驱动。
- Binder驱动:作为内核模块,负责跨进程数据传输,找到目标服务。
- System Server进程:AMS的Binder服务端收到数据,解包后调用真正的
startActivity()方法。
说白了,这就是一次标准的Binder IPC调用。只不过Android在应用层封装了好几层,让你用起来感觉就像调用本地方法一样简单。
下一讲,我们会深入AMS内部,看看它收到请求后是怎么处理Activity栈、怎么创建ActivityRecord、又是怎么回调App进程的。嗯,那才是真正的重头戏。
个人建议:如果你现在对Binder还不太熟悉,建议先花点时间理解Binder的基本原理。不然看AMS的代码会像看天书。我当年就是Binder没吃透,结果在分析Activity启动流程时卡了整整一周。
公众号:蓝海资料掘金营,微信deep3321