以 ClassLoader 为视角看 Android 应用的启动
ActivityManagerService.
- 创建新的进程
startProcessLocked
主要代码如下:
ProcessRecord app = newProcessRecordLocked(info, processName, isolated, isolatedUid);
startProcessLocked(
app, hostingType, hostingNameStr, abiOverride, entryPoint, entryPointArgs);
// 然后
ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, invokeWith, entryPointArgs);
// 然后 Process.start
zygoteProcess.start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
// 然后 ZygoteProcess.startViaZygote
return startViaZygote(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
// 然后 ZygoteProcess.startViaZygote
synchronized(mLock) {
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
// 然后将请求能 Socket 连接发送给 Zygote 读取结果
// Always read the entire result from the input stream to avoid leaving
// bytes in the stream for future process starts to accidentally stumble
// upon.
result.pid = inputStream.readInt();
result.usingWrapper = inputStream.readBoolean();
Zygote
// 1) fork 新的 system_server 进程
int pid = Zygote.forkSystemServer(...)
// 2) handleSystemServerProcess
return handleSystemServerProcess(...)
// 3)
ClassLoader cl = null;
if (systemServerClasspath != null) {
cl = createPathClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion);
Thread.currentThread().setContextClassLoader(cl);
}
/*
* Pass the remaining arguments to SystemServer.
*/
return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
// 4) ZygoteInit 初始化
public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
if (RuntimeInit.DEBUG) {
Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
RuntimeInit.redirectLogStreams();
RuntimeInit.commonInit();
ZygoteInit.nativeZygoteInit();
return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}
从上面 Zygote fork 出来的新的 线程开始初始化时开始涉及 ClassLoader 方面的代码如下:
ClassLoader cl = null;
if (systemServerClasspath != null) {
cl = createPathClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion);
Thread.currentThread().setContextClassLoader(cl);
}
其主要逻辑便是 创建 ClassLoader 然后将其设置为当前线程的的 classLoader。
主要逻辑如下:
/**
* Creates a PathClassLoader for the given class path that is associated with a shared
* namespace, i.e., this classloader can access platform-private native libraries. The
* classloader will use java.library.path as the native library path.
*/
static ClassLoader createPathClassLoader(String classPath, int targetSdkVersion) {
String libraryPath = System.getProperty("java.library.path");
return ClassLoaderFactory.createClassLoader(classPath, libraryPath, libraryPath,
ClassLoader.getSystemClassLoader(), targetSdkVersion, true /* isNamespaceShared */,
null /* classLoaderName */);
}
Thread 与 ContextClassLoader
Returns the context ClassLoader for this Thread. The context
ClassLoader is provided by the creator of the thread for use
by code running in this thread when loading classes and resources.
If not {@linkplain #setContextClassLoader set}, the default is the
ClassLoader context of the parent Thread. The context ClassLoader of the
primordial thread is typically set to the class loader used to load the
application.
也就是在 Android 应用创建之始通过 Thread.current().setContextClassLoader() 设置了类加载器。后面的应用中所有的类默认就通过上面设置的类加载器加载。
后面的启动过程就是通过 ActivityThread
的 main 方法来执行应用线程的主循环
if (entryPoint == null) entryPoint = "android.app.ActivityThread";
值得注意的是其中的一个注释,即它是通过反射的方式来启动应用的主循环,主要原因如下:
Helper class which holds a method and arguments and can call them. This is used as part of
a trampoline to get rid of the initial process setup stack frames.
Application 实例的加载与创建
ActivityThread
的 main 函数在创建了 ActivityThread
实例之后马上进行了 attach 操作。
ActivityThread thread = new ActivityThread();
thread.attach(false);
// 然后
ActivityManagerService.attachApplication(mAppThread);
// 然后双调用回 ActivityThread 的 bindApplication
// 然后 bindApplication 将方法组织好之后通过消息派发 bind_application 任务。
sendMessage(H.BIND_APPLICATION, data);
// 然后
handleBindApplication(AppBindData data)
handleBindApplication
中我们主要关注以下的逻辑处理。
// 1) 创建 Instrumentation 实例
mInstrumentation = new Instrumentation();
// 2) 构造出 Application 实例
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
makeApplication
中的主要逻辑。
// 1) 设置好类加载器
java.lang.ClassLoader cl = getClassLoader();
initializeJavaContextClassLoader();
// 2) 创建 appContext
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
// 3) 创建 Application 对象
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
// 4) 调用 Application.onCreate 方法
instrumentation.callApplicationOnCreate(app);
下面分别 看一下各个主要逻辑的处理细节。
应用加载器的设置
首先是 ClassLoader 的创建
public ClassLoader getClassLoader() {
synchronized (this) {
if (mClassLoader == null) {
createOrUpdateClassLoaderLocked(null /*addedPaths*/);
}
return mClassLoader;
}
}
// 其中 createOrUpdateClassLoaderLocked 主要逻辑如下。
mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip,
mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
libraryPermittedPath, mBaseClassLoader);
// 其中创建了 PathClassLoader 及处理了一些缓存 逻辑。
// 核心呢还是直接通过 PathClassLoader 的构造函数进行创建。
PathClassLoader pathClassloader = new PathClassLoader(dexPath, librarySearchPath, parent);
然后是在 initializeJavaContextClassLoader
中的相关设置。
主要是处理了多进程共享 JVM 资源的一些处理。
/*
* Two possible indications that this package could be
* sharing its virtual machine with other packages:
*
* 1.) the sharedUserId attribute is set in the manifest,
* indicating a request to share a VM with other
* packages with the same sharedUserId.
*
* 2.) the application element of the manifest has an
* attribute specifying a non-default process name,
* indicating the desire to run in another packages VM.
*/
boolean sharedUserIdSet = (pi.sharedUserId != null);
boolean processNameNotDefault =
(pi.applicationInfo != null &&
!mPackageName.equals(pi.applicationInfo.processName));
boolean sharable = (sharedUserIdSet || processNameNotDefault);
ClassLoader contextClassLoader =
(sharable)
? new WarningContextClassLoader()
: mClassLoader;
Thread.currentThread().setContextClassLoader(contextClassLoader);
Application 对象的创建
// 1) 先通过 ClassLoader 根据 application 类名加载对应类。
// 一般来说这就是应用自身代码的第一个 Java 类的加载时机。
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return newApplication(cl.loadClass(className), context);
}
// 2) 通过反射方法 `newInstance` 创建好之后 `attach(context)`
static public Application newApplication(Class<?> clazz, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = (Application)clazz.newInstance();
app.attach(context);
return app;
}
// 3) attach 方法如下, 主要是因为 Application 是一个 ContextWrapper 。 Application 是一个继承自 Context 的类,但是主要的 Context 的操作还是委托给 appContext 即这里的 baseContext。
/* package */ final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
回过头来看一下各种类的加载逻辑
首先能够确定如下观点。
首先我们将上面应用通过 PathClassLoader
创建的类加载器叫做。 appClassLoader
1)应用的第一个类的加载是通过 appClassLoader
来加载的。
2)应用的线程类加载器也被设置成 appClassLoader
来加载。
3)按上面的设置不出意外应用的所有类都是通过 appClassLoader
在 PathClassLoader
中指定的 DexFile
中加载。
此时引出一个问题。JDK 中的其他类是通过什么加载器加载?
根据双亲委托机制。 一般我们认为应该是通过引导加载器加载。
appClassLoader
的父类也有可能就是引导加载器。
/*
* This is the parent we use if they pass "null" in. In theory
* this should be the "system" class loader; in practice we
* don't use that and can happily (and more efficiently) use the
* bootstrap class loader.
*/
ClassLoader baseParent = ClassLoader.getSystemClassLoader().getParent();
而 systemClassLoader 是通过如下代码创建。
return new PathClassLoader(classPath, librarySearchPath, BootClassLoader.getInstance());
BootClassLoader
的主要逻辑交给了 一个名为 classForName
的原生方法来处理。
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
return Class.classForName(name, false, null);
}
// 如下:
/** Called after security checks have been made. */
@FastNative
static native Class<?> classForName(String className, boolean shouldInitialize,
ClassLoader classLoader) throws ClassNotFoundException;