安卓高手之路之ClassLoader(總結篇) - 修補C++ - ITeye技術網站javascript
安卓系統對ClassLoader的設計可謂別有用心。前面分析過,賦值的地方以下:java
- 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(".");
- }
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("."); }
分爲三級: android
Boot 與BOOTCLASSPATH對應bootstrap
System 與CLASSPATH對應。app
App 與應用程序包對應。socket
在應用程序裏面,Context控制着一個ClassLoader,經過創建不一樣的ClassLoader,對外界控制着對APK包的訪問權限。ide
主要有以下幾種:函數
/**
* 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;this/**
* 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;
- }
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();
- }
@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<String, ClassLoader> mLoaders = new HashMap<String, ClassLoader>();
- private static final ApplicationLoaders gApplicationLoaders
- = new ApplicationLoaders();
- }
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<String, ClassLoader> mLoaders = new HashMap<String, ClassLoader>(); 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。