Android应用进程的启动,简单来说就是从zygot进程fork出来一个新进程,并对其进行一些初始化。这样做系统的一些代码和资源等等就不需要重复加载,一些环境变量也都不需要重新设置,可以说是很巧妙的设置。下面就来具体分析一下其初始化过程。
启动时机
应用进程的启动,一般是在创建四大组件,比如说启动Activity,Service,使用ContentProvider,有广播需要处理,这些情况需要创建进程。在我们分析的代码当中,除了这几种情况,BackupAngent也会涉及到创建App进程。
启动进程调用的为AMS当中的startProcessLocked方法, 我们注意看的话,AMS当中还有另一个方法startIsolatedProcess也是用来启动进程的,但是这个方法它启动的进程一般是给系统使用的,我们这里不会分析。
AMS调用启动进程
我们就从AMS的startProcessLocked这个方法开始看起来:
final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
HostingRecord hostingRecord, int zygotePolicyFlags, boolean allowWhileBooting,
boolean isolated) {
return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,
hostingRecord, zygotePolicyFlags, allowWhileBooting, isolated, 0 /* isolatedUid */,
false /* isSdkSandbox */, 0 /* sdkSandboxClientAppUid */,
null /* sdkSandboxClientAppPackage */,
null /* ABI override */, null /* entryPoint */,
null /* entryPointArgs */, null /* crashHandler */);
}
这里我们传入的参数intentFlags为0,zygotePolicyFlags为ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE, allowWhileBooting为false, isolated为false。之后startProcessLocked方法内部用调用了ProcessList的同名方法,其中我们关注的核心语句如下:
ProcessRecord app;
...
app = getProcessRecordLocked(processName, info.uid); //从缓存中获取ProcessRecord
...
if (app == null) {
app = newProcessRecordLocked(info, processName, isolated, isolatedUid, isSdkSandbox,
sdkSandboxUid, sdkSandboxClientAppPackage, hostingRecord);
} else {
app.addPackage(info.packageName, info.longVersionCode, mService.mProcessStats);
}
...
final boolean success =
startProcessLocked(app, hostingRecord, zygotePolicyFlags, abiOverride);
以上代码可以看到,会先去获取是否有现有的processRecord可用,有的话就拿出来使用,没有的话会创建新的,之后会调用startProcessLocked方法。ProcessList中使用mProcessNames来存储ProcessRecord与processName和uid的对应关系,查找的逻辑就是从map中查找不再关注。newProcessRecordLocked方法则是创建新的ProcessRecord,并且会把各种信息保存到这个record当中去。我们这里可以继续看startProcessLocked方法,,最终会调用这个方法:
boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
int zygotePolicyFlags, boolean disableHiddenApiChecks, boolean disableTestApiChecks,
String abiOverride) {
//对于从缓存拿到的ProcessRecord,把原来的信息清掉
if (app.getPid() > 0 && app.getPid() != ActivityManagerService.MY_PID) {
mService.removePidLocked(app.getPid(), app);
app.setBindMountPending(false);
app.setPid(0);
app.setStartSeq(0);
}
app.unlinkDeathRecipient();
app.setDyingPid(0);
...
final IPackageManager pm = AppGlobals.getPackageManager();
permGids pm.getPackageGids(app.info.packageName, =
MATCH_DIRECT_BOOT_AUTO, app.userId);
StorageManagerInternal storageManagerInternal = LocalServices.getService(
StorageManagerInternal.class);
mountExternal = storageManagerInternal.getExternalStorageMountMode(uid,
app.info.packageName); //检查外部存储访问权限
externalStorageAccess = storageManagerInternal.hasExternalStorageAccess(uid,
app.info.packageName);
if (pm.checkPermission(Manifest.permission.INSTALL_PACKAGES,
app.info.packageName, userId)
== PackageManager.PERMISSION_GRANTED) { //检查安装应用的权限
Slog.i(TAG, app.info.packageName + " is exempt from freezer");
app.mOptRecord.setFreezeExempt(true);
}
if (app.processInfo != null && app.processInfo.deniedPermissions != null) {
for (int i = app.processInfo.deniedPermissions.size() - 1; i >= 0; i--) {
int[] denyGids = mService.mPackageManagerInt.getPermissionGids(
app.processInfo.deniedPermissions.valueAt(i), app.userId);
if (denyGids != null) {
for (int gid : denyGids) {
permGids = ArrayUtils.removeInt(permGids, gid);
}
}
}
}
gids = computeGidsForProcess(mountExternal, uid, permGids, externalStorageAccess); //根据前面的权限和相关信息,计算新启动的进程 需要分配的用户
...
//读取app的debuggable,profileable等标志位
boolean debuggableFlag = (app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
boolean isProfileableByShell = app.info.isProfileableByShell();
boolean isProfileable = app.info.isProfileable();
if (debuggableFlag) {
runtimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
runtimeFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE;
runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
if (android.provider.Settings.Global.getInt(mService.mContext.getContentResolver(), android.provider.Settings.Global.ART_VERIFIER_VERIFY_DEBUGGABLE, 1) == 0) {
runtimeFlags |= Zygote.DISABLE_VERIFIER;
}
}
if (isProfileableByShell) {
runtimeFlags |= Zygote.PROFILE_FROM_SHELL;
}
if (isProfileable) {
runtimeFlags |= Zygote.PROFILEABLE;
} //把标志位信息保存到runtimeFlags中
...//其他一些flag写入到runtimeFlags中去
if (debuggableFlag) {
//debuggable时候使用wrap.sh去fork进程
String wrapperFileName = app.info.nativeLibraryDir + "/wrap.sh";
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
try {
if (new File(wrapperFileName).exists()) {
invokeWith = "/system/bin/logwrapper " + wrapperFileName;
}
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
}
String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
if (requiredAbi == null) { //设置 app native 库使用的abi,如arm或者x86或者armv8等等
requiredAbi = Build.SUPPORTED_ABIS[0];
}
String instructionSet = null;
if (app.info.primaryCpuAbi != null) {
instructionSet = VMRuntime.getInstructionSet(requiredAbi);
}
app.setGids(gids);
app.setRequiredAbi(requiredAbi);
app.setInstructionSet(instructionSet); //把信息都设置到ProcessRecord中
final String seInfo = app.info.seInfo
+ (TextUtils.isEmpty(app.info.seInfoUser) ? "" : app.info.seInfoUser);
final String entryPoint = "android.app.ActivityThread"; //设置进程入口位ActivityThread
return startProcessLocked(hostingRecord, entryPoint, app, uid, gids,
runtimeFlags, zygotePolicyFlags, mountExternal, seInfo, requiredAbi,
instructionSet, invokeWith, startUptime, startElapsedTime);
}
以上代码主要是检查应用的各种权限,对其设置对应权限组的groupId,以及设置应用的Abi等信息。之后又会启动一个新的startProcessLocked方法,其中仍然是给ProcessRecord设置参数,其中很大篇幅的为设置debug和profilable相关的参数设置,这里就不列出参数设置的代码了,只列以下最后启动调用的代码:
if (mService.mConstants.FLAG_PROCESS_START_ASYNC) {
mService.mProcStartHandler.post(() -> handleProcessStart(
app, entryPoint, gids, runtimeFlags, zygotePolicyFlags, mountExternal,
requiredAbi, instructionSet, invokeWith, startSeq));
return true;
} else {
final Process.ProcessStartResult startResult = startProcess(hostingRecord,
entryPoint, app,
uid, gids, runtimeFlags, zygotePolicyFlags, mountExternal, seInfo,
requiredAbi, instructionSet, invokeWith, startUptime);
handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
startSeq, false);
return app.getPid() > 0;
}
这里有两个分支,这个 FLAG_PROCESS_START_ASYNC 默认为True,是通过系统的Setting去设置的。第一个分支是通过Handle把任务抛出去执行,而直接返回了执行成功,另一个分支则是等待任务执行完成,在根据返回的UID检查是否成功。不过两个分支里面都是执行了startProcess方法,在这个方法中我们关注以下代码:
if (hostingRecord.usesWebviewZygote()) { //webview进程的创建
startResult = startWebView(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName,
app.getDisabledCompatChanges(),
new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
} else if (hostingRecord.usesAppZygote()) {
final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);
startResult = appZygote.getProcess().start(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName,
/*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
app.getDisabledCompatChanges(), pkgDataInfoMap, allowlistedAppDataInfoMap,
false, false,
new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
} else {
regularZygote = true;
startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
isTopApp, app.getDisabledCompatChanges(), pkgDataInfoMap,
allowlistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
}
以上可以看到我们在创建新的进程的时候,会有三个分支,我们回看我们创建HostingRecord 时候是调用的如下的构造方法:
public HostingRecord(@NonNull String hostingType, ComponentName hostingName, boolean isTopApp) {
this(hostingType, hostingName.toShortString(), REGULAR_ZYGOTE,
null /* definingPackageName */, -1 /* mDefiningUid */, isTopApp /* isTopApp */,
null /* definingProcessName */, null /* action */, TRIGGER_TYPE_UNKNOWN);
}
Process启动进程调用
因此上面的代码是走到了regular分支,它调用了Process的start方法, Process中又调用了ZYGOTE_PROCESS的start方法, ZYGOTE_PROCESS为一个ZygoteProcess常量,其中又会调用startViaZygoate方法,我们来看看这个方法的代码:
private Process.ProcessStartResult startViaZygote(@NonNull final String processClass, @Nullable final String niceName, final int uid, final int gid, @Nullable final int[] gids,
int runtimeFlags, int mountExternal,
int targetSdkVersion,
@Nullable String seInfo,
@NonNull String abi,
@Nullable String instructionSet,
@Nullable String appDataDir,
@Nullable String invokeWith,
boolean startChildZygote,
@Nullable String packageName,
int zygotePolicyFlags,
boolean isTopApp,
@Nullable long[] disabledCompatChanges,
@Nullable Map<String, Pair<String, Long>> pkgDataInfoMap,
@Nullable Map<String, Pair<String, Long>> allowlistedDataInfoList,
boolean bindMountAppsData,
boolean bindMountAppStorageDirs,
@Nullable String[] extraArgs) throws ZygoteStartFailedEx {
ArrayList<String> argsForZygote = new ArrayList<>();
argsForZygote.add("--runtime-args");
argsForZygote.add("--setuid=" + uid);
argsForZygote.add("--setgid=" + gid);
argsForZygote.add("--runtime-flags=" + runtimeFlags);
....
synchronized(mLock) {
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
zygotePolicyFlags,
argsForZygote);
}
上面的代码就是把我们之前所有的各种参数,都拼接起来放到一个字符数组中,后面的openZygoteSocketIfNeeded则是根据abi来于zygote进程建立socket连接,其他的我就要进入zygoteSendArgsAndGetResult方法中查看详情了。
if (shouldAttemptUsapLaunch(zygotePolicyFlags, args)) {
return attemptUsapSendArgsAndGetResult(zygoteState, msgStr);
}
return attemptZygoteSendArgsAndGetResult(zygoteState, msgStr);
这里有一个判断是否要使用usap进程池(非专门app使用进程池),不过我看了这里mUsapPoolEnabled字段默认为false,那我们就不看这个分支了。而attemptZygoteSendArgsAndGetResult代码如下:
private Process.ProcessStartResult attemptZygoteSendArgsAndGetResult(
ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx {
try {
final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;
final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;
zygoteWriter.write(msgStr);
zygoteWriter.flush();
Process.ProcessStartResult result = new Process.ProcessStartResult();
result.pid = zygoteInputStream.readInt();
result.usingWrapper = zygoteInputStream.readBoolean();
if (result.pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
return result;
} catch (IOException ex) {
zygoteState.close();
Log.e(LOG_TAG, "IO Exception while communicating with Zygote - "
+ ex.toString());
throw new ZygoteStartFailedEx(ex);
}
}
从上面的代码我们可以看到,这里其实很简单,就是通过socket向Zytgote发送了我们启动进程需要的参数,然后再通过socket从Zygote读出创建的进程的pid。
Zygote进程创建子进程
这个时候我们需要来看ZygoteInit的main方法,具体zygote进程是如何在系统启动的时候创建的就不去关注了,这里来关注zygote进程如何去创建应用进程的,这里摘抄了一些它的main函数的代码:
preload(bootTimingsTraceLog); //zygote启动之后,预加载代码资源等
zygoteServer = new ZygoteServer(isPrimaryZygote); //创建Zygote 的socket server
caller = zygoteServer.runSelectLoop(abiList); // socket server进入监听状态
if (caller != null) {
caller.run(); //子进程中的时候caller不为空,会执行,此处会执行我们的ActivityThread的main方法,先分析上面的runSelectLoop,其中会有caller的创建
}
runSelectLoop内我们比较关注的代码如下:
Runnable runSelectLoop(String abiList) {
while (true) {
pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
if (pollReturnValue == 0) {
...
} else {
while (--pollIndex >= 0) {
if (pollIndex == 0) {
//如果pollIndex为0,则说明没有socket连接,需要创建socket连接
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
socketFDs.add(newPeer.getFileDescriptor());
} else if (pollIndex < usapPoolEventFDIndex) { //读取Primary socket
ZygoteConnection connection = peers.get(pollIndex);
boolean multipleForksOK = !isUsapPoolEnabled() && ZygoteHooks.isIndefiniteThreadSuspensionSafe();
final Runnable command = connection.processCommand(this, multipleForksOK);
if (mIsForkChild) {
return command; //子进程,返回command
} else {
//父进程的一些处理
}
...
}
....
}
...
}
}
}
上面的代码省略了一些如果是Usap进程的代码,代码里面有两层的循环,在内层循环中,以pollIndex作为循环的条件,如果pollIndex为0,在acceptCommandPeer中会建立新的Socket Connet,代码里面就是一个ZygoteConnection。如果存在Connect的情况下会,会通过判断当前pollIndex是否小于usapPollEventFDIndex来判断是否是普通的进程创建,之后会调用connection.processCommand来读取socket数据做后续的处理,代码如下:
ZygoteArguments parsedArgs;
try (ZygoteCommandBuffer argBuffer = new ZygoteCommandBuffer(mSocket)) {
while (true) {
parsedArgs = ZygoteArguments.getInstance(argBuffer);
...
if (parsedArgs.mInvokeWith != null || parsedArgs.mStartChildZygote
|| !multipleOK || peer.getUid() != Process.SYSTEM_UID) {
pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid,
parsedArgs.mGids, parsedArgs.mRuntimeFlags, rlimits,
parsedArgs.mMountExternal, parsedArgs.mSeInfo, parsedArgs.mNiceName,
fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
parsedArgs.mInstructionSet, parsedArgs.mAppDataDir,
parsedArgs.mIsTopApp, parsedArgs.mPkgDataInfoList,
parsedArgs.mAllowlistedDataInfoList, parsedArgs.mBindMountAppDataDirs,
parsedArgs.mBindMountAppStorageDirs); //fork子进程的操作
try {
if (pid == 0) { //子进程处理分支
zygoteServer.setForkChild();
zygoteServer.closeServerSocket();
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
return handleChildProc(parsedArgs, childPipeFd,
parsedArgs.mStartChildZygote);
} else { //父进程处理分支
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
handleParentProc(pid, serverPipeFd);
return null;
}
} finally {
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
} else {
...
}
...
}
}
上面代码是处理socket数据的代码,我这里省略了除了创建进程之外的处理其他操作的代码。其中我们可以看到系统是使用了ZygoteArguments来解析我们之前从system_server进程传过来的参数,之后调用Zygote.forkAndSpecialize来创建进程,在linux中,fork完进程之后,是通过pid来判断当前是在父进程还是子进程中的,当前为子进程则pid为0。forkAndSpecialize方法中主要是调用了nativeForkAndSpecialize,这个是native方法,代码在com_android_internal_os_Zygote.cpp中,在native中的方法为com_android_internal_os_Zygote_nativeForkAndSpecialize我们去看看它的代码。
...
pid_t pid = zygote::ForkCommon(env, /* is_system_server= */ false, fds_to_close, fds_to_ignore, true);
if (pid == 0) {
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, capabilities, capabilities,
mount_external, se_info, nice_name, false, is_child_zygote == JNI_TRUE,
instruction_set, app_data_dir, is_top_app == JNI_TRUE, pkg_data_info_list,
allowlisted_data_info_list, mount_data_dirs == JNI_TRUE,
mount_storage_dirs == JNI_TRUE);
}
return pid;
上面的代码可以看到,第一行是去fork子进程,后面会判断是否为子进程,如果为子进程则会为子进程做一些处理。其中我省略了前面一部分fds_to_close 和fds_to_ignore赋值的代码,那些为需要关闭或者忽略的文件描述符,会传到这个ForkCommon方法中,我们具体看看这个方法:
SetSignalHandlers(); //设置错误信号监听
BlockSignal(SIGCHLD, fail_fn); //暂时关闭SIGCHLD信号,方便后面关闭fd
__android_log_close(); //关闭log相关的FD
AStatsSocket_close();
...
pid_t pid = fork(); //调用系统调用执行fork进程
if (pid == 0) {
...
PreApplicationInit(); //子进程的初始化,主要是设置当前进程不是zygote进程
DetachDescriptors(env, fds_to_close, fail_fn); //把传进来的要关闭的fd关掉
...
} else {
...
}
UnblockSignal(SIGCHLD, fail_fn); //重新打开之前关闭的SIGCHLD信号
return pid;
可以看到上面的代码主要是去调用fork系统调用去从zygote进程fork一个新进程作为应用使用的进程,而SpecializeCommon,我们根据传入的参数和代码可以知道,其中主要是设置子进程的用户组,以及挂载应用目录,一些其他相关的初始化,就不分析其代码了。然后我们就可以继续会到java代码。
创建完子进程后的操作
在前面processCommand方法中,我们知道fork成功之后如果是子进程会执行handleChildProc方法,如果是父进程会执行handleParentProc方法,先来看一下父进程执行的代码:
if (pid > 0) {
setChildPgid(pid);
}
...
mSocketOutStream.writeInt(pid);
mSocketOutStream.writeBoolean(usingWrapper);
这个方法中我们需要关注的就上面这一部分代码,首先是把这个子进程的pid放到进程的当前进程的孩子进程组中去。后面的就是把子进程的pid和是否使用了wrapper写入到socket中,这样我们之前请求创建进程那个地方就能拿到子进程的id了。
再来看子进程所执行的handleChildProc方法:
closeSocket();
Zygote.setAppProcessName(parsedArgs, TAG);
if (parsedArgs.mInvokeWith != null) {
WrapperInit.execApplication(parsedArgs.mInvokeWith,
parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
VMRuntime.getCurrentInstructionSet(),
pipeFd, parsedArgs.mRemainingArgs);
// Should not get here.
throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
} else {
if (!isZygote) {
return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mDisabledCompatChanges,
parsedArgs.mRemainingArgs, null /* classLoader */);
} else {
return ZygoteInit.childZygoteInit(
parsedArgs.mRemainingArgs /* classLoader */);
}
}
首先第一行是关闭socket,前面的native代码其实已经关闭过了socket,但是在java层还是有LocalSocket,也需要关闭。
第二行就是给我们这个进程设置名称。
后面的第一个判断是看我们是否使用wrapper,正常流程不会走到这里,else分支中我们这里也不是fork一个新的zygote进程,因此也只需要看ZygoteInit.zygoteInit这个方法即可。
RuntimeInit.redirectLogStreams(); //关闭默认的log,设置使用android的print来输出system.out和system.error的log
RuntimeInit.commonInit();
ZygoteInit.nativeZygoteInit();
return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
classLoader);
App进程的初始化
RuntimeInit.commonInit中是一些初始化,包括错误处理,时区,网络的userAgent等,不看代码了。nativeZygoteInit的代码在AndroidRuntime.cpp中
static void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env, jobject clazz)
{
gCurRuntime->onZygoteInit();
}
此处调用了gCurRuntime的onZygoteInit()方法,而这个方法是AndroidRuntime中的一个虚方法,在app_main.cpp中我们看到实际上对于应用我们有一个子类AppRuntime中实现了这个方法,代码如下:
virtual void onZygoteInit()
{
sp<ProcessState> proc = ProcessState::self();
ALOGV("App process: starting thread pool.\n");
proc->startThreadPool();
}
我们之前分析binder的时候,知道ProcessState这个类binder是有使用的,调用self方法会打开binder驱动,这个代码里面是为binder创建应用进程的线程池,具体这里就不分析了。继续看RuntimeInit.applicationInit代码:
protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
String[] argv, ClassLoader classLoader) {
nativeSetExitWithoutCleanup(true);
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);
final Arguments args = new Arguments(argv);
return findStaticMain(args.startClass, args.startArgs, classLoader);
}
上面的方面,前面的代码主要是设置targetSdkversion和其他的一些设置,我们主要来看后面的findStaticMain方法:
protected static Runnable findStaticMain(String className, String[] argv,
ClassLoader classLoader) {
Class<?> cl;
try {
cl = Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
"Missing class when invoking static main " + className,
ex);
}
Method m;
try {
m = cl.getMethod("main", new Class[] { String[].class });
} catch (NoSuchMethodException ex) {
throw new RuntimeException(
"Missing static main on " + className, ex);
} catch (SecurityException ex) {
throw new RuntimeException(
"Problem getting static main on " + className, ex);
}
int modifiers = m.getModifiers();
if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new RuntimeException(
"Main method is not public and static on " + className);
}
return new MethodAndArgsCaller(m, argv);
}
可以看到我们通过反射拿到应用的之前设置的应用入口,也就是ActivityThread类,之后再获取到它的main方法,最后组装成一个MethodAndArgsCaller对象,最后返回。从前面的代码我们知道它会在ZygoteInit的main方法中执行。然后我们就可以来分析ActivityThread代码了。
ActivityThread代码执行
public static void main(String[] args) {
AndroidOs.install();
Environment.initForCurrentUser();
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
initializeMainlineModules();
Looper.prepareMainLooper();
...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
上面的代码可以看到是为应用进程做一些初始化,首先是为sys call使用android的一些定制,其次是指定CA证书的位置,之后安装Mainline的模块,后面是初始化looper进入Looper循环,这样应用的主线程也就完成了初始化。在启动loop之前有一个attach方法,对于应用进程我们传进来的第一个参数为false, 也就是非系统进程,我们来看代码,只看应用进程的分支。
RuntimeInit.setApplicationObject(mAppThread.asBinder());
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
BinderInternal.addGcWatcher(new Runnable() {
public void run() {
//监听gc,当可用内存比较小的时候尝试回收一些Activity
}
}
ViewRootImpl.ConfigChangedCallback configChangedCallback = (Configuration globalConfig) -> {
//config 变化的回调,用来更新app得而configuration
}
ViewRootImpl.addConfigCallback(configChangedCallback);
上面的代码主要做了四件事情,其中两个是注册gc的回调和view configration 变化的回调,我们最关注的是调用ActivityManager的atttachApplication,和RuntimeInit的setApplicationObject,它们都用到了mAppThread,这个对象为ApplicationThread,而它是IApplicationThread.aidl的客户端实现。这里首先是把它的IBinder对象传到RuntimeInit中,这样发生一些事情的时候系统可以通知到应用。
回到AMS
另外我们再来看一下ActivityManager的attchApplication方法,它实际调用的是ActivityManagerService的attachApplication方法,在它内部又调用了attachApplicationLocked方法,这里只看一下我们比较关心的一部分代码:
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
List<ProviderInfo> providers = normalMode
? mCpHelper.generateApplicationProvidersLocked(app)
: null;
...
final ProviderInfoList providerList = ProviderInfoList.fromList(providers);
...
thread.bindApplication(processName, appInfo,
app.sdkSandboxClientAppVolumeUuid, app.sdkSandboxClientAppPackage,
providerList, null, profilerInfo, null, null, null, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.isPersistent(),
new Configuration(app.getWindowProcessController().getConfiguration()),
app.getCompat(), getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial, autofillOptions, contentCaptureOptions,
app.getDisabledCompatChanges(), serializedSystemFontMap,
app.getStartElapsedTime(), app.getStartUptime());
...
if (normalMode) {
try {
didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
badApp = true;
}
}
if (!badApp) {
try {
didSomething |= mServices.attachApplicationLocked(app, processName);
checkTime(startTime, "attachApplicationLocked: after mServices.attachApplicationLocked"); //检查是否有Service需要在当前进程启动
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown starting services in " + app, e);
badApp = true;
}
}
if (!badApp && isPendingBroadcastProcessLocked(pid)) {
try {
didSomething |= sendPendingBroadcastsLocked(app); //发送pending的广播
checkTime(startTime, "attachApplicationLocked: after sendPendingBroadcastsLocked");
} catch (Exception e) {
// If the app died trying to launch the receiver we declare it 'bad'
Slog.wtf(TAG, "Exception thrown dispatching broadcasts in " + app, e);
badApp = true;
}
}
上面省略了一些代码,不过我们application需要做的一些核心代码都还在。除了列出的代码外,这里其实还有一些pending service启动,pending 广播的执行,以及ContentProvider的安装等,这些我们先略过。
首先这个normalMode的判断,我们假设当前已经是使用中而不是刚启动手机,而mProcessesReady是在system_server启动之后就赋值为true了,所以对于app启动的状况来说,这里normalMode为true。这里我们需要重点关注的就两个地方,一个是thread.bindApplication,另一处是mAtmInternal.attachApplication。bindApplication会通过binder调用到应用进程的bindApplication方法。
AppBindData data = new AppBindData();
data.processName = processName;
....
sendMessage(H.BIND_APPLICATION, data);
这里主要就去构建了AppBindData,使用ActivityThread内部的H来发送消息,消息回调处会调用ActivityThread的handleBindApplication方法。这个方法的代码非常多,前面的一些是设置包名,进程名称等等信息,以及configration信息以及调试器相关的东西,这些我们都不关注,这里跳过。
data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo, isSdkSandbox); //构建apk信息,创建LoadApk对象。
//创建AppContent,并且把confirmration绑定到Context上
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
mConfigurationController.updateLocaleListFromAppContext(appContext);
//创建Instrumentation
mInstrumentation = new Instrumentation();
mInstrumentation.basicInit(this);
if (!data.restrictedBackupMode) { //执行安装ContentProvider
if (!ArrayUtils.isEmpty(data.providers)) {
installContentProviders(app, data.providers);
}
}
//创建程序的Application
app = data.info.makeApplicationInner(data.restrictedBackupMode, null);
//调用Application的onCreate方法
mInstrumentation.callApplicationOnCreate(app);
上面的逻辑我们需要关注makeApplicationInner, 后面的callApplicationOnCreate内部就是调用Application的onCreate方法,不再分析了。
final Application cached = sApplications.get(mPackageName);
if (cached != null) {
if (!allowDuplicateInstances) {
mApplication = cached;
return cached;
}
}
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
上面的代码主要是从缓存里面取Application,如果没有则通过Instrumentaion去创建新的Applicaion,我们继续去看newApplication的代码
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
String appClass = mApplicationInfo.getCustomApplicationClassNameForProcess(
myProcessName);
Application app = getFactory(context.getPackageName())
.instantiateApplication(cl, className);
app.attach(context);
return app;
}
这里的代码比较简单,就是拿到AppComponentFactory然后通过反射创建App的Application对象,之后调用app的attach方法,attach方法内部会调用attachBaseContext方法。就不往里去看代码了。
对于需要启动Activity的情况,我们需要看ActivityManagerService的attachApplication,我们需要再看一下mAtmInternal.attachApplication。它会调用ActivityTaskManagerService的内部类LocalService的方法,内部会调用mRootWindowContainer.attachApplication(wpc);,它的内部又会调用mAttachApplicationHelper.process(app),内部又会调用ensureActivitiesVisible方法,一路看进去最终会调用EnsureActivitiesVisibleHelper的process方法,它的内部会调用setActivityVisibilityState。
if (!r.attachedToProcess()) {
makeVisibleAndRestartIfNeeded(mStarting, mConfigChanges, isTop,
resumeTopActivity && isTop, r);
} else if (r.isVisibleRequested()) {
// If this activity is already visible, then there is nothing to do here.
if (DEBUG_VISIBILITY) {
Slog.v(TAG_VISIBILITY, "Skipping: already visible at " + r);
}
if (r.mClientVisibilityDeferred && mNotifyClients) {
r.makeActiveIfNeeded(r.mClientVisibilityDeferred ? null : starting);
r.mClientVisibilityDeferred = false;
}
r.handleAlreadyVisible();
if (mNotifyClients) {
r.makeActiveIfNeeded(mStarting);
}
} else {
r.makeVisibleIfNeeded(mStarting, mNotifyClients);
}
这些就是去执行启动Activity相关的逻辑,这里也先略过。
以下是AMS发起创建新进程的时序图:
sequenceDiagram
AMS->>+ProcessList: startProcessLocked
ProcessList->>ProcessList: newProcessRecordLocked
ProcessList->>ProcessList: startProcess
ProcessList->>+ZygoteProcess: startViaZygote
ZygoteProcess->>ZygoteProcess: openZygoteSocketIfNeeded
ZygoteProcess->>ZygoteProcess: zygoteSendArgsAndGetResult
ZygoteProcess->>ZygoteServer: send args and get Result
ZygoteProcess-->>-ProcessList: return result with pid
ProcessList-->>-AMS: return ProcessRecord
以下是Zygote侧处理fork进程请求的时序图:
sequenceDiagram
ZygoteInit->>ZygoteServer: runSelectLoop
loop 无限循环
ZygoteServer->>ZygoteConnection: processCommand
ZygoteConnection->>Zygote: forkAndSpecialize
ZygoteConnection-->>ZygoteConnection: (Parent): notify child pid by socket
ZygoteServer-->>ZygoteInit: return command(child process)
end
rect rgb(191, 223, 255)
note right of ZygoteInit: fork完子进程执行的内容
ZygoteConnection->>ZygoteInit: zygoteInit
ZygoteInit->>ZygoteInit: nativeZygoteInit
ZygoteInit->>AppRuntime: onZygoteInit
ZygoteInit->>RuntimeInit: findStaticMain
RuntimeInit-->>ZygoteInit: return ActivityThread Main function caller
ZygoteInit->>ActivityThread: main
ActivityThread->>AMS: attachApplication
AMS->>ActivityThread: bindApplication
end
以上就是应用进程启动的完整流程,为了使得流程更加简洁,其中不太重要的步骤有作省略。如果你也对于Android系统源码感兴趣,欢迎与我交流。博文因为个人局限,也难免会出现差错,欢迎大家指正。