• DexClassLoader和PathClassLoader类载入机制


        0x00

        在DexClassLoader和PathClassLoader载入Dex流程一文中,我们分析了dex文件怎样形成了DexFile结构体。本文中解说类载入机制,实际上就是生成ClassObject对象。

        我们以DexClassLoader为例。解说类载入机制,PathClassLoader是一样的。

        我们在载入类时一般会调用loadClass,那么我们就从loadClass来開始分析。

        

        0x01

        DexClassLoader类没有loadClass方法。所以调用的是父类ClassLoader类的loadClass方法,ClassLoader类的loadClass方法位于libcorelunisrcmainjavajavalangClassLoader.java中。

        protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
            Class<?> clazz = findLoadedClass(className);
    
            if (clazz == null) {
                try {
                    clazz = parent.loadClass(className, false);
                } catch (ClassNotFoundException e) {
                    // Don't want to see this.
                }
    
                if (clazz == null) {
                    clazz = findClass(className);
                }
            }
    
            return clazz;
        }
        DexClassLoader复写了父类ClassLoader的findClass方法。所以调用子类DexClassLoader类的方法findClass。代码位于libcoredalviksrcmainjavadalviksystemDexClassLoader.java。
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            if (VERBOSE_DEBUG)
                System.out.println("DexClassLoader " + this
                    + ": findClass '" + name + "'");
    
            int length = mFiles.length;
    
            for (int i = 0; i < length; i++) {
                if (VERBOSE_DEBUG)
                    System.out.println("  Now searching: " + mFiles[i].getPath());
    
                if (mDexs[i] != null) {
                    String slashName = name.replace('.', '/');
                    Class clazz = mDexs[i].loadClass(slashName, this);
                    if (clazz != null) {
                        if (VERBOSE_DEBUG)
                            System.out.println("    found");
                        return clazz;
                    }
                }
            }
    
            throw new ClassNotFoundException(name + " in loader " + this);
        }
        这里调用的是DexFile类的loadClass方法,代码位于libcoredalviksrcmainjavadalviksystemDexFile.java。

        public Class loadClass(String name, ClassLoader loader) {
            String slashName = name.replace('.', '/');
            return loadClassBinaryName(slashName, loader);
        }
        public Class loadClassBinaryName(String name, ClassLoader loader) {
            return defineClass(name, loader, mCookie,
                null);
                //new ProtectionDomain(name) /*DEBUG ONLY*/);
        }
        defineClass相应的是JNI方法,例如以下:

    native private static Class defineClass(String name, ClassLoader loader,
            int cookie, ProtectionDomain pd);
        还记得DexClassLoader和PathClassLoader载入Dex流程一文中,openDexFile也是JNI方法。相应的native方法位于dalvikvm ativedalvik_system_DexFile.c。

    const DalvikNativeMethod dvm_dalvik_system_DexFile[] = {
        { "openDexFile",        "(Ljava/lang/String;Ljava/lang/String;I)I",
            Dalvik_dalvik_system_DexFile_openDexFile },
        { "closeDexFile",       "(I)V",
            Dalvik_dalvik_system_DexFile_closeDexFile },
        { "defineClass",        "(Ljava/lang/String;Ljava/lang/ClassLoader;ILjava/security/ProtectionDomain;)Ljava/lang/Class;",
            Dalvik_dalvik_system_DexFile_defineClass },
        { "getClassNameList",   "(I)[Ljava/lang/String;",
            Dalvik_dalvik_system_DexFile_getClassNameList },
        { "isDexOptNeeded",     "(Ljava/lang/String;)Z",
            Dalvik_dalvik_system_DexFile_isDexOptNeeded },
        { NULL, NULL, NULL },
    };
    

        defineClass相应的是Dalvik_dalvik_system_DexFile_defineClass方法。注意defineClass函数传递进来的參数有一个是mCookie,就是在DexClassLoader和PathClassLoader载入Dex流程一文中。openDexFile生成的,利用这个mCookie能够在native层找到openDexFile生成的DexFile结构体。


        0x02

        Dalvik_dalvik_system_DexFile_defineClass代码位于dalvikvm ativedalvik_system_DexFile.c

    static void Dalvik_dalvik_system_DexFile_defineClass(const u4* args,
        JValue* pResult)
    {
        StringObject* nameObj = (StringObject*) args[0];
        Object* loader = (Object*) args[1];
        int cookie = args[2];
        Object* pd = (Object*) args[3];
        ClassObject* clazz = NULL;
        DexOrJar* pDexOrJar = (DexOrJar*) cookie;
        DvmDex* pDvmDex;
        char* name;
        char* descriptor;
    
        name = dvmCreateCstrFromString(nameObj);
        descriptor = dvmDotToDescriptor(name);
        LOGV("--- Explicit class load '%s' 0x%08x
    ", descriptor, cookie);
        free(name);
    
        if (!validateCookie(cookie))
            RETURN_VOID();
    
        if (pDexOrJar->isDex)
            pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile);
        else
            pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile);
    
        /* once we load something, we can't unmap the storage */
        pDexOrJar->okayToFree = false;
    
        clazz = dvmDefineClass(pDvmDex, descriptor, loader);
        ......
    
        ......
    
        free(descriptor);
        RETURN_PTR(clazz);
    }
        首先通过cookie找到DexOrJar结构体pDexOrJar,然后依据pDexOrJar找到DvmDex结构体pDvmDex。

        以下我们来分析核心函数dvmDefineClass,这个用来生成ClassObject。dvmDefineClass。findClassNoInit 方法都位于dalvikvmooClass.c。

    ClassObject* dvmDefineClass(DvmDex* pDvmDex, const char* descriptor,
        Object* classLoader)
    {
        assert(pDvmDex != NULL);
    
        return findClassNoInit(descriptor, classLoader, pDvmDex);
    }
    static ClassObject* findClassNoInit(const char* descriptor, Object* loader,
        DvmDex* pDvmDex)
    {
        Thread* self = dvmThreadSelf();
        ClassObject* clazz;
        bool profilerNotified = false;
    
        ......
        clazz = dvmLookupClass(descriptor, loader, true);
        if (clazz == NULL) {
            const DexClassDef* pClassDef;
    
            ......
    
            if (pDvmDex == NULL) {
                assert(loader == NULL);     /* shouldn't be here otherwise */
                pDvmDex = searchBootPathForClass(descriptor, &pClassDef);
            } else {
                pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);
            }
    
            ......
    
            /* found a match, try to load it */
            clazz = loadClassFromDex(pDvmDex, pClassDef, loader);
            ......
            if (!dvmAddClassToHash(clazz)) {
                ......
            }
    		......
    	}
        return clazz;
    }
        首先调用dvmLookupClass方法,依据目标类的描写叙述符descriptor在系统已载入类中进行查找,如果已对其载入,则返回目标类的ClassObject对象;否则,将对目标类进行载入。

        我们如果没有对其载入过,然后调用dexFindClass方法找到DexClassDef结构体。我们首先来看下DexClassDef结构体,代码位于dalvikvmooClass.c。

    typedef struct DexClassDef {
        u4  classIdx;           /* index into typeIds for this class */
        u4  accessFlags;
        u4  superclassIdx;      /* index into typeIds for superclass */
        u4  interfacesOff;      /* file offset to DexTypeList */
        u4  sourceFileIdx;      /* index into stringIds for source file name */
        u4  annotationsOff;     /* file offset to annotations_directory_item */
        u4  classDataOff;       /* file offset to class_data_item */
        u4  staticValuesOff;    /* file offset to DexEncodedArray */
    } DexClassDef;
        为了方便理解以后的代码,我这里先附上一张图。DexClassDef就是图中最左边的部分class_def_item。



        dexFindClass方法也位于dalvikvmooClass.c。

    const DexClassDef* dexFindClass(const DexFile* pDexFile,
        const char* descriptor)
    {
        const DexClassLookup* pLookup = pDexFile->pClassLookup;
        u4 hash;
        int idx, mask;
    
        hash = classDescriptorHash(descriptor);
        mask = pLookup->numEntries - 1;
        idx = hash & mask;
    
        /*
         * Search until we find a matching entry or an empty slot.
         */
        while (true) {
            int offset;
    
            offset = pLookup->table[idx].classDescriptorOffset;
            if (offset == 0)
                return NULL;
    
            if (pLookup->table[idx].classDescriptorHash == hash) {
                const char* str;
    
                str = (const char*) (pDexFile->baseAddr + offset);
                if (strcmp(str, descriptor) == 0) {
                    return (const DexClassDef*)
                        (pDexFile->baseAddr + pLookup->table[idx].classDefOffset);
                }
            }
    
            idx = (idx + 1) & mask;
        }
    }
        最后返回值的地方解释下。pDexFile->baseAddr指向dex文件头部。后面加上的是DexClassDef结构体距离dex文件头部的偏移。

        

        返回到findClassNoInit,继续运行loadClassFromDex方法。这是真正生成ClassObject对象的地方。

    代码位于dalvikvmooClass.c。

    static ClassObject* loadClassFromDex(DvmDex* pDvmDex,
        const DexClassDef* pClassDef, Object* classLoader)
    {
        ClassObject* result;
        DexClassDataHeader header;
        const u1* pEncodedData;
        const DexFile* pDexFile;
    
        assert((pDvmDex != NULL) && (pClassDef != NULL));
        pDexFile = pDvmDex->pDexFile;
    
        if (gDvm.verboseClass) {
            LOGV("CLASS: loading '%s'...
    ",
                dexGetClassDescriptor(pDexFile, pClassDef));
        }
    
        pEncodedData = dexGetClassData(pDexFile, pClassDef);
    
        if (pEncodedData != NULL) {
            dexReadClassDataHeader(&pEncodedData, &header);
        } else {
            // Provide an all-zeroes header for the rest of the loading.
            memset(&header, 0, sizeof(header));
        }
    
        result = loadClassFromDex0(pDvmDex, pClassDef, &header, pEncodedData,
                classLoader);
    
        if (gDvm.verboseClass && (result != NULL)) {
            LOGI("[Loaded %s from DEX %p (cl=%p)]
    ",
                result->descriptor, pDvmDex, classLoader);
        }
    
        return result;
    }
        dexGetClassData方法用来获取上图中的第二部分class_data_item。

    代码位于dalviklibdexDexFile.h。

    DEX_INLINE const u1* dexGetClassData(const DexFile* pDexFile,
        const DexClassDef* pClassDef)
    {
        if (pClassDef->classDataOff == 0)
            return NULL;
        return (const u1*) (pDexFile->baseAddr + pClassDef->classDataOff);
    }
        loadClassFromDex0用于生成终于的ClassObject对象。代码位于dalviklibdexDexFile.h。

    static ClassObject* loadClassFromDex0(DvmDex* pDvmDex,
        const DexClassDef* pClassDef, const DexClassDataHeader* pHeader,
        const u1* pEncodedData, Object* classLoader)
    {
        ClassObject* newClass = NULL;
        const DexFile* pDexFile;
        const char* descriptor;
        int i;
    
        pDexFile = pDvmDex->pDexFile;
        descriptor = dexGetClassDescriptor(pDexFile, pClassDef);
    
        /*
         * Make sure the aren't any "bonus" flags set, since we use them for
         * runtime state.
         */
        if ((pClassDef->accessFlags & ~EXPECTED_FILE_FLAGS) != 0) {
            LOGW("Invalid file flags in class %s: %04x
    ",
                descriptor, pClassDef->accessFlags);
            return NULL;
        }
    
        /*
         * Allocate storage for the class object on the GC heap, so that other
         * objects can have references to it.  We bypass the usual mechanism
         * (allocObject), because we don't have all the bits and pieces yet.
         *
         * Note that we assume that java.lang.Class does not override
         * finalize().
         */
        /* TODO: Can there be fewer special checks in the usual path?

    */ assert(descriptor != NULL); if (classLoader == NULL && strcmp(descriptor, "Ljava/lang/Class;") == 0) { assert(gDvm.classJavaLangClass != NULL); newClass = gDvm.classJavaLangClass; } else { size_t size = classObjectSize(pHeader->staticFieldsSize); newClass = (ClassObject*) dvmMalloc(size, ALLOC_DEFAULT); } if (newClass == NULL) return NULL; DVM_OBJECT_INIT(&newClass->obj, gDvm.classJavaLangClass); dvmSetClassSerialNumber(newClass); newClass->descriptor = descriptor; assert(newClass->descriptorAlloc == NULL); newClass->accessFlags = pClassDef->accessFlags; dvmSetFieldObject((Object *)newClass, offsetof(ClassObject, classLoader), (Object *)classLoader); newClass->pDvmDex = pDvmDex; newClass->primitiveType = PRIM_NOT; newClass->status = CLASS_IDX; /* * Stuff the superclass index into the object pointer field. The linker * pulls it out and replaces it with a resolved ClassObject pointer. * I'm doing it this way (rather than having a dedicated superclassIdx * field) to save a few bytes of overhead per class. * * newClass->super is not traversed or freed by dvmFreeClassInnards, so * this is safe. */ assert(sizeof(u4) == sizeof(ClassObject*)); /* 32-bit check */ newClass->super = (ClassObject*) pClassDef->superclassIdx; /* * Stuff class reference indices into the pointer fields. * * The elements of newClass->interfaces are not traversed or freed by * dvmFreeClassInnards, so this is GC-safe. */ const DexTypeList* pInterfacesList; pInterfacesList = dexGetInterfacesList(pDexFile, pClassDef); if (pInterfacesList != NULL) { newClass->interfaceCount = pInterfacesList->size; newClass->interfaces = (ClassObject**) dvmLinearAlloc(classLoader, newClass->interfaceCount * sizeof(ClassObject*)); for (i = 0; i < newClass->interfaceCount; i++) { const DexTypeItem* pType = dexGetTypeItem(pInterfacesList, i); newClass->interfaces[i] = (ClassObject*)(u4) pType->typeIdx; } dvmLinearReadOnly(classLoader, newClass->interfaces); } /* load field definitions */ /* * Over-allocate the class object and append static field info * onto the end. It's fixed-size and known at alloc time. This * seems to increase zygote sharing. Heap compaction will have to * be careful if it ever tries to move ClassObject instances, * because we pass Field pointers around internally. But at least * now these Field pointers are in the object heap. */ if (pHeader->staticFieldsSize != 0) { /* static fields stay on system heap; field data isn't "write once" */ int count = (int) pHeader->staticFieldsSize; u4 lastIndex = 0; DexField field; newClass->sfieldCount = count; for (i = 0; i < count; i++) { dexReadClassDataField(&pEncodedData, &field, &lastIndex); loadSFieldFromDex(newClass, &field, &newClass->sfields[i]); } } if (pHeader->instanceFieldsSize != 0) { int count = (int) pHeader->instanceFieldsSize; u4 lastIndex = 0; DexField field; newClass->ifieldCount = count; newClass->ifields = (InstField*) dvmLinearAlloc(classLoader, count * sizeof(InstField)); for (i = 0; i < count; i++) { dexReadClassDataField(&pEncodedData, &field, &lastIndex); loadIFieldFromDex(newClass, &field, &newClass->ifields[i]); } dvmLinearReadOnly(classLoader, newClass->ifields); } /* * Load method definitions. We do this in two batches, direct then * virtual. * * If register maps ha

  • 相关阅读:
    SVN使用指引(Windows)
    Android开发14——监听内容提供者ContentProvider的数据变化
    Android开发13——内容提供者ContentProvider的基本使用
    Android开发12——Andorid中操作数据库的insert的两种方法以及nullColumnHack
    Android开发11——手机横屏和竖屏与android:configChanges
    Android开发10——Activity的跳转与传值
    Android开发9——Activity的启动模式
    Android开发8——利用pull解析器读写XML文件
    Android开发7——android.database.CursorIndexOutOfBoundsException:Index -1 requested
    Android开发6——布局中的wrap_content和fill_parent以及match_parent
  • 原文地址:https://www.cnblogs.com/wzzkaifa/p/6984776.html
Copyright © 2020-2023  润新知