安卓系统对ClassLoader的设计可谓别有用心。前面分析过,赋值的地方如下:
const char* envStr = getenv("CLASSPATH");
if (envStr != NULL) {
gDvm.classPathStr = strdup(envStr);
} else {
gDvm.classPathStr = strdup(".");
}
envStr = getenv("BOOTCLASSPATH");
if (envStr != NULL) {
gDvm.bootClassPathStr = strdup(envStr);
} else {
gDvm.bootClassPathStr = strdup(".");
}
分为三级:
Boot 与BOOTCLASSPATH对应
System 与CLASSPATH对应。
App 与应用程序包对应。
在应用程序里面,Context控制着一个ClassLoader,通过建立不同的ClassLoader,对外界控制着对APK包的访问权限。
主要有如下几种:
/**
* Flag for use with {@link #createPackageContext}: include the application
* code with the context. This means loading code into the caller's
* process, so that {@link #getClassLoader()} can be used to instantiate
* the application's classes. Setting this flags imposes security
* restrictions on what application context you can access; if the
* requested application can not be safely loaded into your process,
* java.lang.SecurityException will be thrown. If this flag is not set,
* there will be no restrictions on the packages that can be loaded,
* but {@link #getClassLoader} will always return the default system
* class loader.
*/
public static final int CONTEXT_INCLUDE_CODE = 0x00000001;
/**
* Flag for use with {@link #createPackageContext}: ignore any security
* restrictions on the Context being requested, allowing it to always
* be loaded. For use with {@link #CONTEXT_INCLUDE_CODE} to allow code
* to be loaded into a process even when it isn't safe to do so. Use
* with extreme care!
*/
public static final int CONTEXT_IGNORE_SECURITY = 0x00000002;
/**
* Flag for use with {@link #createPackageContext}: a restricted context may
* disable specific features. For instance, a View associated with a restricted
* context would ignore particular XML attributes.
*/
public static final int CONTEXT_RESTRICTED = 0x00000004;
1.ClassLoader如其名,就是加载class用的。
2.一开始的时候,是通过dalvik/vm/Jni.cpp中的FindClass函数来找类的。
NativeStart是一个假类,里面的main是java堆栈的root。
第一。系统启动
因为这段代码是C++的代码,那么可以肯定一定是java(或者NativeStart这个假main函数)调用过来的。
具体谁调用过来的,这里做了个判断:
如果是NativeStart.main:
这个时候要进行初始化判断,有可能vm还没有进行初始化。
如果是System.nativeload
这个时候,就用classLoaderOverride
如果是其他:
就是 thisMethod->clazz->classLoader 也就是 加载这段代码的classloader
分别进入了三个不同的分支。
static jclass FindClass(JNIEnv* env, const char* name) {
ScopedJniThreadState ts(env);
const Method* thisMethod = dvmGetCurrentJNIMethod();
assert(thisMethod != NULL);
Object* loader;
Object* trackedLoader = NULL;
if (ts.self()->classLoaderOverride != NULL) {
/* hack for JNI_OnLoad */
assert(strcmp(thisMethod->name, "nativeLoad") == 0);
loader = ts.self()->classLoaderOverride;
} else if (thisMethod == gDvm.methDalvikSystemNativeStart_main ||
thisMethod == gDvm.methDalvikSystemNativeStart_run) {
/* start point of invocation interface */
if (!gDvm.initializing) {
loader = trackedLoader = dvmGetSystemClassLoader();
} else {
loader = NULL;
}
} else {
loader = thisMethod->clazz->classLoader;
}
char* descriptor = dvmNameToDescriptor(name);
if (descriptor == NULL) {
return NULL;
}
ClassObject* clazz = dvmFindClassNoInit(descriptor, loader);
free(descriptor);
jclass jclazz = (jclass) addLocalReference(ts.self(), (Object*) clazz);
dvmReleaseTrackedAlloc(trackedLoader, ts.self());
return jclazz;
}
这种classLoade是BootClassLoader.
第二 app启动。
app启动 通过socket完成。通过fork来创建一个子进程。这个时候ClassLoader是与Context有关的。
不同的Context对应不同的ClassLoader。这个ClassLoader是一个PathClassLoader
@Override
public ClassLoader getClassLoader() {
return mPackageInfo != null ?
mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader();
}
package android.app;
import dalvik.system.PathClassLoader;
import java.util.HashMap;
import java.util.Map;
class ApplicationLoaders
{
public static ApplicationLoaders getDefault()
{
return gApplicationLoaders;
}
public ClassLoader getClassLoader(String zip, String libPath, ClassLoader parent)
{
/*
* 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();
synchronized (mLoaders) {
if (parent == null) {
parent = baseParent;
}
/*
* If we're one step up from the base class loader, find
* something in our cache. Otherwise, we create a whole
* new ClassLoader for the zip archive.
*/
if (parent == baseParent) {
ClassLoader loader = mLoaders.get(zip);
if (loader != null) {
return loader;
}
PathClassLoader pathClassloader =
new PathClassLoader(zip, libPath, parent);
mLoaders.put(zip, pathClassloader);
return pathClassloader;
}
return new PathClassLoader(zip, parent);
}
}
private final Map mLoaders = new HashMap();
private static final ApplicationLoaders gApplicationLoaders
= new ApplicationLoaders();
}
具体来讲ClassLoader.getSystemClassLoader() 返回的是一个PathClassLoader,而
ClassLoader.getSystemClassLoader().getParent() 返回的是一个BootClassLoader。
如果LoadedApk这个类在构造的时候,传入了个BootClassLoader或者null,那么就会执行
PathClassLoader pathClassloader =
new PathClassLoader(zip, libPath, parent);
mLoaders.put(zip, pathClassloader);
return pathClassloader;
也就是说 把libPath进行了传入。
否则用如下构造函数执行
return new PathClassLoader(zip, parent);
可以看到,少了一个参数libPath,libPath是libjni。那么这个是什么意思呢?
经过看代码,默认LoadedAPK传入的loader是个null, 因此,会使用libPath进行构造。并且它的父Loader是BootClassLoader。那么什么时候传入的loader不是nul呢。用instrument的时候传入的不是null。