startActivity启动流程的源码学习
举例,当进程A调用startActivity方法,启动进程B,并打开B的Activity,这个过程是怎样的?以下是学习笔记,基于Android 9.0,在线源码查看:https://www.androidos.net.cn/android/9.0.0_r8/xref
- 进程A调用startActivity方法,本质上是通过binder通信,调用IActivityManager#startActivity方法
- 如果该调用在Activity中,方法调用过程:
ContextImpl#startActivity -> mInstrumentation#execStartActivity -> IActivityManager#startActivity
备注:IActivityManager对象保存在进程A的单例IActivityManagerSingleton中,进程启动时查询ServiceManager#getService获得,具有缓存作用
- 如果该调用在Activity中,方法调用过程:
- SystemServer进程的binder线程响应此请求,ActivityManagerService#startActivity方法被调用,过程如下(备注:过程较复杂,涉及Activity,ActivityStack等一连串操作):
- ActivityManagerService#startActivity
- ActivityStarter#startActivityMayWait,其中会调用PMS#resolveIntent方法,解析Intent获得ResolveInfo,并创建ActivityInfo等对象
ResolveInfo rInfo = mSupervisor.resolveIntent((intent, resolvedType, userId, xxx);
- ActivityStarter#startActivity,其中会创建即将打开的ActivityRecord等
ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid, xxxx);
- ActivityStack#resumeTopActivityUncheckedLocked
- ActivityStackSupervisor#startSpecificActivityLocked,这里很重要,会根据目标进程名,查询进程是否已存在,如果存在,将执行realStartActivityLocked方法;如果不存在,将执行startProcessLocked,冷启动目标进程
void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) { // Is this activity's application already running? ProcessRecord app = mService.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid, true); ...... if (app != null && app.thread != null) { ....... realStartActivityLocked(r, app, andResume, checkConfig); return; } ...... mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
- 假设这里要启动新进程,执行
ActivityManagerService#startProcessLocked -> ActivityManagerService#startProcess -> Process#start -> ZygoteProcess#startViaZygote
a. 利用Socket通信通知Zygote进程,孵化出进程B(判断是否启动成功:Socket返回结果中目标进程pid < 0,则启动失败,抛出ZygoteStartFailedEx异常)
b. 进程B启动成功后,方法继续执行到ActivityManagerService#handleProcessStartedLocked,其中pid等信息填入事先创建好的ProcessRecord对象中,其对应每一个App进程,保存在数组mPidsSelfLocked中mPidsSelfLocked是AMS的成员变量,类型:SparseArray
,本质是利用2个数组存储,key和object各一个。此处pid为key,ProcessRecord对象为object。查询方法为二分法
- 目标进程B启动后,入口函数是ActivityThread#main(静态方法),过程如下:
- 初始化环境,创建ActivityThread对象,其子对象中如:
a. mH,类型:ActivityThread#H,作用:主线程的Handler,处理如BIND_APPLICATION,CREATE_SERVICE等几十个消息类型,消息来源是mAppThread
b. mAppThread,类型:ActivityThread#ApplicationThread,作用:binder实体对象,用于接收AMS的binder调用,并将消息转发mH处理
c. mInstrumentation,类型:Instrumentation,很重要的基础类,官方描述:用来监控系统与应用的所有交互,例如newApplication,callApplicationOnCreate,callActivityOnCreate等都是其子方法
注:平时解bug时发现,一些App的插件化hook框架,会动态替换mInstrumentation对象,如达到启动Activity时替换目标页面等目的
- 调用ActivityThread#attach方法 [重要],其中调用IActivityManager#attachApplication,与AMS进行binder通信,将进程的mAppThread传给AMS,类似进行注册
mAppThread,类型:ApplicationThread extends IApplicationThread.Stub,本质为binder对象,作为服务端,将来接收AMS的binder调用
- main方法最后调用Looper.loop,进程B的主线程进入循环处理消息模式
- 初始化环境,创建ActivityThread对象,其子对象中如:
public static void main(String[] args) {
// 3.1(Binder对象,继承自IApplicationThread.Stub)
Environment.initForCurrentUser();
Looper.prepareMainLooper();
// 3.2
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
// 3.3
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
【startActivity启动流程的源码学习】
- SystemServer进程,作为服务端,接收到进程B的attachApplication方法调用,过程如下:
- 从前面提到的mPidsSelfLocked中提取app对象,类型:ProcessRecord,存入applicationThread等信息
- 注册死亡回调,以便App进程挂掉后,AMS能及时得到通知,并清除App的相关信息
- 发起binder通信,利用applicationThread对象,调用进程B的ApplicationThread#bindApplication方法,经消息转发,最终由进程B主线程的mH对象处理,消息类型:BIND_APPLICATION
- 检查是否有页面要显示
ActivityStackSupervisor#attachApplicationLocked ->ActivityStackSupervisor#realStartActivityLocked(如果进程B有位于栈顶的Activity需显示) -> ClientTransaction#schedule
利用applicationThread对象,调用进程B的ApplicationThread#scheduleTransaction方法,经消息转发,最终由进程B主线程的mH对象处理,操控Activity生命周期等,消息类型:EXECUTE_TRANSACTION - 检查是否有服务要启动
ActiveServices#attachApplicationLocked -> ActivityStackSupervisor#realStartServiceLocked
利用applicationThread对象,调用进程B的ApplicationThread#scheduleCreateService方法,后续同上类似 - 检查是否有广播消息要处理,不再详细描述
- 检查是否有BackupAgent要创建,不再详细描述
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid, int callingUid, long startSeq) {
// 4.1
ProcessRecord app;
if (pid != MY_PID && pid >= 0) {
app = mPidsSelfLocked.get(pid);
// 4.2
try {
AppDeathRecipient adr = new AppDeathRecipient(
app, pid, thread);
thread.asBinder().linkToDeath(adr, 0);
app.deathRecipient = adr;
} catch (RemoteException e) {
// ...
}
// 4.3
thread.bindApplication(processName, appInfo, providers, xxx);
// 4.4
mStackSupervisor.attachApplicationLocked(app)) {
didSomething = true;
}
// 4.5
didSomething |= mServices.attachApplicationLocked(app, processName);
// 4.6
didSomething |= sendPendingBroadcastsLocked(app);
// 4.7
thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, xxx);
return true;
}
总结: 至此经前4步,至少5次关键的跨进程通信,startActivity过程已基本完成。简单描述:
- 进程A调用IActivityManager#startActivity方法,发起binder通信
- SystemServer进程作为服务端,AMS会检查进程B是否存在,如无,发起Socket通信,通知Zgyote进程孵化出进程B
- 进程B启动后,调用IActivityManager#attachApplication方法,发起binder通信
- SystemServer进程作为服务端,AMS会保存进程B的相关信息,发起binder通信,调用进程B的bindApplication,scheduleTransaction等方法
- 进程B依次完成Application#onCreate等初始化,并执行scheduleTransaction,初始化要显示的Activity,过程结束
作者:kevin song,2018.11.18于南京建邺区
推荐阅读
- Hive常见问题汇总
- 注册分销商的骄傲
- 迅捷流程图制作软件的使用方法!
- 如何启动改变
- spring|spring boot项目启动websocket
- Android系统启动之init.rc文件解析过程
- Python专栏|数据分析的常规流程
- mysql提示无法找到句饼_找不到数据库启动句柄
- 2018-03-11|2018-03-11 存储过程
- 简述JavaEE学习流程