捋一捋 Android 启动应用进程的前因后果
作者:互联网
点击桌面应用图标后
前面(Android 进阶解密阅读笔记2)已经知道启动应用程序进程是由 AMS 向 zygote 进程发起申请,后面由 zygote 进程监听处理。但一般场景下,用户只有点了桌面应用图标才会打开应用,可见 AMS 也是收到了某种响应才会发起申请。在 Android 进阶解密阅读笔记3 中我们知道启动根 Activity 最终会走到 ActivityStackSupervisor 的 startSpecificActivityLocked 方法,而这个方法中的逻辑也包含着启动应用进程,这就是比 AMS 发起创建应用进程请求更早的地方。(以下代码基于 API 28)
void startSpecificActivityLocked(ActivityRecord r,boolean andResume, boolean checkConfig) {
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,"activity", r.intent.getComponent(), false, false,true);
}
大致可见,AMS 根据应用进程的一些信息去获取 ProcessRecord。如果启动根 Activity 时,发现 app 为 null 的话就会调用 AMS 启动应用进程。那在应用进程启动后并调用 ActivityThread 的 main,这个 main 具体又做了什么呢?
ActivityThread 的 main 方法做了什么
public static void main(String[] args) {
//简单来说 main 方法里创建了主线程的消息循环
Looper.prepareMainLooper();
//另外一件事就在这里
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if(sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
}
private void attach(boolean system, long startSeq) {
final IActivityManager mgr = ActivityManager.getService();
try {
//通过 AMS 代理类去绑定应用
mgr.attachApplication(mAppThread, startSeq);
} catch(RemoteException ex) {
throw ex.rethorwFromSystemServer();
}
}
在 AMS 做了一些逻辑后,又会交还给 ActivityThread 的内部类 ApplicationThread 来处理。
public final void bindApplication(...) {
//通过消息机制,发送 BIND_APPLICATION 的消息交给 mH 处理
AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
data.providers = providers;
data.instrumentationName = instrumentationName;
//省略部分...
sendMessage(H.BIND_APPLICATION, data);
}
通过消息机制,最终会交给 ActivityThread 对象去调用 handleBindApplication 方法,这里为什么通过消息机制呢?我想是不是做到了进程切换,即进程间通信。由 AMS 所在的 SystemServer 进程进入了应用进程。「不是很肯定,后面再查资料证实」
handleBindApplication 方法很大,这里只了解大致逻辑。1.加载 InstrumentationInfo,注释说会影响 class loader,要事先加载。2.生成 ApplicationInfo, LoadedApk 用于创建 ContextImpl 对象实例,而这个实例用于创建 class loader。前面加载的 InstrumentationInfo 用在了 ApplicationInfo 的创建。3.接着用 class loader 加载 Instrumentation 对象实例,并做 init 操作。4.通过 LoadedApk 对象间接创建 Application 对象,实际上是调用了 ActivityThread 对象的 mInstrumentation 对象的 newApplication 方法。5.再往下调用了 mInstrumentation 对象的 callApplicationOnCreate 方法,此时也就触发了 Application 对象的 onCreate 方法
创建 Application
//LoadedApk
public Application makeApplication(boolean forceDefaultAppClass,Instrumentation instrumentation) {
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);
}
//Instrumentation
public Application newApplication(ClassLoader cl, String className, Context context) {
Application app = getFactory(context.getPackageName()).instantiateApplication(cl, className);
//这里传入的 context 就是 Application 调用 *getBaseContext* 获取到的 mBase
app.attach(context);
return app;
}
小结
到这我们理清楚了三个东西,1.启动应用进程时最初的出发点就是 Launcher 响应用户点击应用图标的操作。2.Application 在何时创建,并何时调用了 onCreate 方法。3.Application 类是继承自 ContextWrapper 的,其方法 getBaseContext 的 mBase 对象实例是何时实现赋值的。
不过还有一点还没理清楚,就是我们最终是想打开应用的,目前由于应用进程没启动,我们去启动应用进程了,那根 Activity 怎么继续启动呢?
再回到启动应用进程
//AMS
final ProcessRecord startProcessLocked(...太多了,省略...) {
ProcessRecord app;
if(!isolated) {
//方法内通过应用进程名称从 mProcessNames 这个 ProcessMap 结构中获取 app
//按当前情况应该获取不到返回 null
app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
}
if(app == null) {
//创建新的 ProcessRecord 对象
app = newProcessRecordLocked(info, processName, isolated, isolatedUid);
}
//开始启动应用进程
final boolean success = startProcessLocked(app, hostingType, hostingNameStr, abiOverride);
return success ? app : null;
}
final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess, boolean isolated, int isolatedUid) {
final ProcessRecord r = new ProcessRecord(this, stats, info, proc, uid);
//这里是将新建的 ProcessRecord 保存起来
addProcessNameLocked(r);
return r;
}
private final void addProcessNameLocked(ProcessRecord proc) {
//保险起见,先从缓存里做移除操作,正常来说是没有的
ProcessRecord old = removeProcessNameLocked(proc.processName, proc.uid);
// UidRecord 存着进程 Uid 相关信息
UidRecord uidRec = mActiveUids.get(proc.uid);
if(uidRec == null) {
uidRec = new UidRecord(proc.uid);
mActiveUids.put(proc.uid, uidRec);
}
proc.uidRecord = uidRec;
//把新建的 processRecord 存起来
mProcessNames.put(proc.processName, proc.uid, proc);
if(proc.isolated) {
mIsolatedProcesses.put(proc.uid, proc);
}
}
//中间还会再调一层 startProcessLocked 方法,接着就到下面的方法了
private final boolean startProcessLocked(ProcessRecord app, String hostingType,String hostingNameStr, boolean disableHiddenApiChecks, String abiOverride) {
if(app.pid > 0 && app.pid != MY_PID) {
synchronized(mPidsSelfLocked) {
//mPidsSelfLocked 这个东西类似 mProcessNames 是个集合用来维护进程
//这里做了移除,可见后面应该会有添加
mPidsSelfLocked.remove(app.pid);
}
}
//这里继续启动应用进程
return startProcessLocked(hostingType, hostingNameStr, entryPoint, app, uid, gids, runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet, invokeWith, startTime);
}
private boolean startProcessLocked(String hostingType, String hostingNameStr, String entryPoint,ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,String seInfo, String requiredAbi, String instructionSet, String invokeWith,long startTime) {
//这里会继续启动应用进程,并返回启动结果
final ProcessStartResult startResult = startProcess(app.hostingType, entryPoint,app, app.startUid, gids, runtimeFlags, mountExternal, app.seInfo,requiredAbi, instructionSet, invokeWith, app.startTime);
synchronized(ActivityManagerService.this) {
//看下这个启动结果怎么处理
handleProcessStartedLocked(app, startResult, startSeq);
}
}
private boolean handleProcessStartedLocked(ProcessRecord pending,
ProcessStartResult startResult, long expectedStartSeq) {
//再进一层看看
return handleProcessStartedLocked(pending, startResult.pid,
startResult.usingWrapper,expectedStartSeq, false);
}
private boolean handleProcessStartedLocked(ProcessRecord app, int pid, boolean usingWrapper,long expectedStartSeq, boolean procAttached) {
ProcessRecord oldApp;
synchronized(mPidsSelfLocked) {
//这里类似之前的操作,取出已有的做清除操作,但这种情况一般是没有的
oldApp = mPidsSelfLocked.get(pid);
}
synchronized(mPidsSelfLocked) {
//关键的一句,终于看见它把新建的 processRecord 存进去了
this.mPidsSelfLocked.put(pid, app);
}
}
为什么要关注 mPidsSelfLocked 呢,后面就会知道了。
来看应用进程启动后执行 ActivityThread 对象实例的 attach 方法
//AMS
//最后又来到了 AMS 里
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid, int callingUid, long startSeq) {
ProcessRecord app;
//因为 AMS 是在 SystemServer 进程创建的,所以 pid != 当前进程ID
if(pid != MY_PID && pid > 0) {
synchronized(mPidsSelfLocked) {
//这里就是之前启动应用进程时存起来的 processRecord,
//这么一来 app 不就有值了么
app = mPidsSelfLocked.get(pid);
}
}
//这里就会去调用 ApplicationThread 的方法,其实就是用消息机制处理
thread.bindApplication(...);
if(normalMode) {
try{
//这里就是看看是否有 Activity 等待启动
//显然我们启动应用进程就是因为要启动 Activity
if(mStackSupervisor.attachApplicationLocked(app)) {
didSomething = true;
}
}
}
if(!badApp) {
try {
//这里粗看起来是用来启动 Service 服务的
didSomething |= mServices.attachApplicationLocked(app, processName);
}
}
if(!badApp && isPendingBroadcastProcessLocked(pid)) {
try {
//这里用来启动广播
didSomething |= sendPendingBroadcastsLocked(app);
}
}
}
//ActivityStackSupervisor
boolean attachApplicationLocked(ProcessRecord app) {
final ActivityRecord top = stack.topRunningActivityLocked();
final int size = mTmpActivityList.size();
for (int i = 0; i < size; i++) {
final ActivityRecord activity = mTmpActivityList.get(i);
if (activity.app == null && app.uid == activity.info.applicationInfo.uid
&& processName.equals(activity.processName)) {
try {
//这个不就是启动 Activity 的最终方法么
if(realStartActivityLocked(activity, app, top == activity, true)) {
didSomething = true;
}
}
}
}
return didSomething;
}
这么看来,应用进程启动后,会通过事先保存的 ProcessRecord 继续启动之前想要启动的 Activity 的。并且这也解释了,Application 的 onCreate 方法会先于 Activity 的 onCreate 执行。
参考文章
Android应用程序启动过程源代码分析
标签:ProcessRecord,启动,一捋,app,boolean,前因后果,进程,Android,proc 来源: https://blog.csdn.net/fsalwen/article/details/113194563