安卓高手之路之ClassLoader(总结篇)

来源:互联网 发布:藤井莉娜的淘宝店 编辑:程序博客网 时间:2024/06/06 11:43

安卓系统对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<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。

 

原创粉丝点击