android应用程序启动流程分析
android应用程序启动流程
当在launcher进程(home进程)中点击一个应用的图标时其会调用startActivity,然后会通过Binder与system_server系统进程跨进程通讯。system_server会调用Process.start开始启动一个新进程。Process.start会先调用Porcess.StartViaZygote函数
Porcess.StartViaZygote函数会进一步调用Process.zygoteSendArgsAndGetResult函数打开一个socket连接。
在zygote进程中会监控此socket连接,监听返回后会调用
if (parsedArgs.mInvokeWith != null || parsedArgs.mStartChildZygote
|| !multipleOK || peer.getUid() != Process.SYSTEM_UID) {
// Continue using old code for now. TODO: Handle these cases in the other path.
//调用forkAndSpecialize去fork一个新进程
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);
try {
if (pid == 0) {
// in child
//如果是子进程就调用handleParentProc()
zygoteServer.setForkChild();
zygoteServer.closeServerSocket();
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
return handleChildProc(parsedArgs, childPipeFd,
parsedArgs.mStartChildZygote);
} else {
// In the parent. A pid < 0 indicates a failure and will be handled in
// handleParentProc.
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
handleParentProc(pid, serverPipeFd);
return null;
}
} finally {
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
}
handlerChildProc最终会调用findStaticMain去加载android.app.ActivityThread类并通过反射调用其对应的静态main方法
protected static Runnable findStaticMain(String className, String[] argv,
ClassLoader classLoader) {
Class<?> cl;
try {
//加载android.app.ActivityThread类
cl = Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
"Missing class when invoking static main " + className,
ex);
}
Method m;
try {
//得到android.app.ActivityThread类的静态main方法Method对象
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);
}
//最后通过反射调用android.app.ActivityThread类的静态main方法
return new MethodAndArgsCaller(m, argv);
}
ActivityThread.main方法
ActivityThread的静态main方法中会创建一个ActivityThread实例对象,然后进行初始化,最终进入loop主消息循环来接收系统的消息。
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// Install selective syscall interception
AndroidOs.install();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
// Call per-process mainline module initialization.
initializeMainlineModules();
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
//实例化一个ActivityThread对象
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//进入消息循环
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
system_server会通过Binder来远程过程调用函数handlebindapplication。
handlebindapplication会先调用Application.attachBaseContext函数,然后调用Application.onCreate函数。所以一个app进程最先执行的就是这两个函数中代码。
private void handleBindApplication(AppBindData data) {
//step 1: 创建LoadedApk对象
data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
...
//step 2: 创建ContextImpl对象;
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
//step 3: 创建Instrumentation
mInstrumentation = new Instrumentation();
//step 4: 创建Application对象;在makeApplication函数中调用了newApplication,在该函数中又调用了app.attach(context),在attach函数中调用了Application.attachBaseContext函数
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
//step 5: 安装providers
List<ProviderInfo> providers = data.providers;
installContentProviders(app, providers);
//step 6: 执行Application.onCreate回调
mInstrumentation.callApplicationOnCreate(app);
注意makeApplication函数用来创建一个Application函数并调用其attachBaseContext函数。
- 其会先判断第一个参数是否为null,如果不是就直接返回mApplication对象,
- 如果是false就会判断mApplication是否已经加载,如果已经加载就直接返回,否则就new一个Application并加入到mAllApplications中。(所以在加壳apk中在Application时,创建原apk的Application前要先将mApplication删除并从mAllApplication中移除,然后调用makeApplication并传递null)
public final class LoadedApk {
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if (mApplication != null) {
return mApplication;
}
Application app = null;
//判断mApplication是否已经加载
String appClass = mApplicationInfo.className;
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}
try {
java.lang.ClassLoader cl = getClassLoader();
....
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
} catch (Exception e) {
....
}
mActivityThread.mAllApplications.add(app);
mApplication = app;
....
return app;
}
}
public class Instrumentation {
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = getFactory(context.getPackageName())
.instantiateApplication(cl, className);
app.attach(context);
return app;
}
}