安卓高手之路之ClassLoader(總結篇)

安卓高手之路之ClassLoader(總結篇) - 修補C++ - ITeye技術網站javascript

安卓系統對ClassLoader的設計可謂別有用心。前面分析過,賦值的地方以下:java

Java代碼   收藏代碼
  1. const char* envStr = getenv("CLASSPATH");  
  2.   if (envStr != NULL) {  
  3.       gDvm.classPathStr = strdup(envStr);  
  4.   } else {  
  5.       gDvm.classPathStr = strdup(".");  
  6.   }  
  7.   envStr = getenv("BOOTCLASSPATH");  
  8.   if (envStr != NULL) {  
  9.       gDvm.bootClassPathStr = strdup(envStr);  
  10.   } else {  
  11.       gDvm.bootClassPathStr = strdup(".");  
  12.   }  
  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

分別進入了三個不一樣的分支。

 

Cpp代碼   收藏代碼
  1. static jclass FindClass(JNIEnv* env, const char* name) {  
  2.     ScopedJniThreadState ts(env);  
  3.   
  4.     const Method* thisMethod = dvmGetCurrentJNIMethod();  
  5.     assert(thisMethod != NULL);  
  6.   
  7.     Object* loader;  
  8.     Object* trackedLoader = NULL;  
  9.     if (ts.self()->classLoaderOverride != NULL) {  
  10.         /* hack for JNI_OnLoad */  
  11.         assert(strcmp(thisMethod->name, "nativeLoad") == 0);  
  12.         loader = ts.self()->classLoaderOverride;  
  13.     } else if (thisMethod == gDvm.methDalvikSystemNativeStart_main ||  
  14.                thisMethod == gDvm.methDalvikSystemNativeStart_run) {  
  15.         /* start point of invocation interface */  
  16.         if (!gDvm.initializing) {  
  17.             loader = trackedLoader = dvmGetSystemClassLoader();  
  18.         } else {  
  19.             loader = NULL;  
  20.         }  
  21.     } else {  
  22.         loader = thisMethod->clazz->classLoader;  
  23.     }  
  24.   
  25.     char* descriptor = dvmNameToDescriptor(name);  
  26.     if (descriptor == NULL) {  
  27.         return NULL;  
  28.     }  
  29.     ClassObject* clazz = dvmFindClassNoInit(descriptor, loader);  
  30.     free(descriptor);  
  31.   
  32.     jclass jclazz = (jclass) addLocalReference(ts.self(), (Object*) clazz);  
  33.     dvmReleaseTrackedAlloc(trackedLoader, ts.self());  
  34.     return jclazz;  
  35. }  
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

       

Java代碼   收藏代碼
  1. @Override  
  2.   public ClassLoader getClassLoader() {  
  3.       return mPackageInfo != null ?  
  4.               mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader();  
  5.   }  
  @Override
    public ClassLoader getClassLoader() {
        return mPackageInfo != null ?
                mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader();
    }

 

Java代碼   收藏代碼
  1. package android.app;  
  2.   
  3. import dalvik.system.PathClassLoader;  
  4.   
  5. import java.util.HashMap;  
  6. import java.util.Map;  
  7.   
  8. class ApplicationLoaders  
  9. {  
  10.     public static ApplicationLoaders getDefault()  
  11.     {  
  12.         return gApplicationLoaders;  
  13.     }  
  14.   
  15.     public ClassLoader getClassLoader(String zip, String libPath, ClassLoader parent)  
  16.     {  
  17.         /* 
  18.          * This is the parent we use if they pass "null" in.  In theory 
  19.          * this should be the "system" class loader; in practice we 
  20.          * don't use that and can happily (and more efficiently) use the 
  21.          * bootstrap class loader. 
  22.          */  
  23.         ClassLoader baseParent = ClassLoader.getSystemClassLoader().getParent();  
  24.   
  25.         synchronized (mLoaders) {  
  26.             if (parent == null) {  
  27.                 parent = baseParent;  
  28.             }  
  29.   
  30.             /* 
  31.              * If we're one step up from the base class loader, find 
  32.              * something in our cache.  Otherwise, we create a whole 
  33.              * new ClassLoader for the zip archive. 
  34.              */  
  35.             if (parent == baseParent) {  
  36.                 ClassLoader loader = mLoaders.get(zip);  
  37.                 if (loader != null) {  
  38.                     return loader;  
  39.                 }  
  40.       
  41.                 PathClassLoader pathClassloader =  
  42.                     new PathClassLoader(zip, libPath, parent);  
  43.                   
  44.                 mLoaders.put(zip, pathClassloader);  
  45.                 return pathClassloader;  
  46.             }  
  47.   
  48.             return new PathClassLoader(zip, parent);  
  49.         }  
  50.     }  
  51.   
  52.     private final Map<String, ClassLoader> mLoaders = new HashMap<String, ClassLoader>();  
  53.   
  54.     private static final ApplicationLoaders gApplicationLoaders  
  55.         = new ApplicationLoaders();  
  56. }  
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。

相關文章
相關標籤/搜索