1. 程式人生 > >DexClassLoader和PathClassLoader載入Dex流程

DexClassLoader和PathClassLoader載入Dex流程

    0x00

    在上一篇文章apk安裝和優化原理,在最後我們分析了DexClassLoader和PathClassLoader的建構函式的不同。

    PathClassLoader最後呼叫的是new DexFile(pathFile),而DexClassLoader呼叫的是DexFile.loadDex(dexPathList[i], outputName, 0)。

    0x01

    new DexFile(pathFile)對應的程式碼位於libcore\dalvik\src\main\java\dalvik\system\DexFile.java。

    public DexFile(String fileName) throws IOException {
        String wantDex = System.getProperty("android.vm.dexfile", "false");
        if (!wantDex.equals("true"))
            throw new UnsupportedOperationException("No dex in this VM");

        mCookie = openDexFile(fileName, null, 0);
        mFileName = fileName;
        //System.out.println("DEX FILE cookie is " + mCookie);
    }
    DexFile.loadDex(dexPathList[i], outputName, 0)對應的程式碼也位於libcore\dalvik\src\main\java\dalvik\system\DexFile.java。
    static public DexFile loadDex(String sourcePathName, String outputPathName,
        int flags) throws IOException {

        /*
         * TODO: we may want to cache previously-opened DexFile objects.
         * The cache would be synchronized with close().  This would help
         * us avoid mapping the same DEX more than once when an app
         * decided to open it multiple times.  In practice this may not
         * be a real issue.
         */
        return new DexFile(sourcePathName, outputPathName, flags);
    }
    private DexFile(String sourceName, String outputName, int flags)
        throws IOException {

        String wantDex = System.getProperty("android.vm.dexfile", "false");
        if (!wantDex.equals("true"))
            throw new UnsupportedOperationException("No dex in this VM");

        mCookie = openDexFile(sourceName, outputName, flags);
        mFileName = sourceName;
        //System.out.println("DEX FILE cookie is " + mCookie);
    }
    我們可以看到其實兩者最終呼叫的都是openDexFile,只不過DexClassLoader指定了生成優化後的apk路徑,而PathClassLoader則不需要,因為在安裝階段已經生成了/data/dalvik-cache/[email protected]

    0x02

    我們繼續分析openDexFile,這個方法一個JNI方法。

    native private static int openDexFile(String sourceName, String outputName,
        int flags) throws IOException;
   程式碼位於libcore\dalvik\src\main\java\dalvik\system\DexFile.java。

    對應的native方法位於dalvik\vm\native\dalvik_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 },
};
    我們看到openDexFile對應的是Dalvik_dalvik_system_DexFile_openDexFile方法。
static void Dalvik_dalvik_system_DexFile_openDexFile(const u4* args,
    JValue* pResult)
{
    ......
    if (dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) {
        LOGV("Opening DEX file '%s' (DEX)\n", sourceName);

        pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
        pDexOrJar->isDex = true;
        pDexOrJar->pRawDexFile = pRawDexFile;
    } else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) {
        LOGV("Opening DEX file '%s' (Jar)\n", sourceName);

        pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
        pDexOrJar->isDex = false;
        pDexOrJar->pJarFile = pJarFile;
    } else {
        LOGV("Unable to open DEX file '%s'\n", sourceName);
        dvmThrowException("Ljava/io/IOException;", "unable to open DEX file");
    }

    ......

    RETURN_PTR(pDexOrJar);
}
    程式碼位於dalvik\vm\native\dalvik_system_DexFile.c。

    這裡呼叫dvmJarFileOpen,程式碼也位於dalvik\vm\JarFile.c中。

int dvmJarFileOpen(const char* fileName, const char* odexOutputName,
    JarFile** ppJarFile, bool isBootstrap)
{
    ZipArchive archive;
    DvmDex* pDvmDex = NULL;
    char* cachedName = NULL;
    bool archiveOpen = false;
    bool locked = false;
    int fd = -1;
    int result = -1;

    /* Even if we're not going to look at the archive, we need to
     * open it so we can stuff it into ppJarFile.
     */
    if (dexZipOpenArchive(fileName, &archive) != 0)
        goto bail;
    archiveOpen = true;

    /* If we fork/exec into dexopt, don't let it inherit the archive's fd.
     */
    dvmSetCloseOnExec(dexZipGetArchiveFd(&archive));

    /* First, look for a ".odex" alongside the jar file.  It will
     * have the same name/path except for the extension.
     */
    fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName);
    if (fd >= 0) {
       .......
    } else {
        ZipEntry entry;

tryArchive:
        /*
         * Pre-created .odex absent or stale.  Look inside the jar for a
         * "classes.dex".
         */
        entry = dexZipFindEntry(&archive, kDexInJarName);
        if (entry != NULL) {
            bool newFile = false;

            /*
             * We've found the one we want.  See if there's an up-to-date copy
             * in the cache.
             *
             * On return, "fd" will be seeked just past the "opt" header.
             *
             * If a stale .odex file is present and classes.dex exists in
             * the archive, this will *not* return an fd pointing to the
             * .odex file; the fd will point into dalvik-cache like any
             * other jar.
             */
            if (odexOutputName == NULL) {
                cachedName = dexOptGenerateCacheFileName(fileName,
                                kDexInJarName);
                if (cachedName == NULL)
                    goto bail;
            } else {
                cachedName = strdup(odexOutputName);
            }
            LOGV("dvmDexCacheStatus: Checking cache for %s (%s)\n",
                fileName, cachedName);
            fd = dvmOpenCachedDexFile(fileName, cachedName,
                    dexGetZipEntryModTime(&archive, entry),
                    dexGetZipEntryCrc32(&archive, entry),
                    isBootstrap, &newFile, /*createIfMissing=*/true);
            if (fd < 0) {
                LOGI("Unable to open or create cache for %s (%s)\n",
                    fileName, cachedName);
                goto bail;
            }
            locked = true;

            /*
             * If fd points to a new file (because there was no cached version,
             * or the cached version was stale), generate the optimized DEX.
             * The file descriptor returned is still locked, and is positioned
             * just past the optimization header.
             */
            if (newFile) {
                u8 startWhen, extractWhen, endWhen;
                bool result;
                off_t dexOffset;

                dexOffset = lseek(fd, 0, SEEK_CUR);
                result = (dexOffset > 0);

                if (result) {
                    startWhen = dvmGetRelativeTimeUsec();
                    result = dexZipExtractEntryToFile(&archive, entry, fd) == 0;
                    extractWhen = dvmGetRelativeTimeUsec();
                }
                if (result) {
                    result = dvmOptimizeDexFile(fd, dexOffset,
                                dexGetZipEntryUncompLen(&archive, entry),
                                fileName,
                                dexGetZipEntryModTime(&archive, entry),
                                dexGetZipEntryCrc32(&archive, entry),
                                isBootstrap);
                }

                if (!result) {
                    LOGE("Unable to extract+optimize DEX from '%s'\n",
                        fileName);
                    goto bail;
                }

                endWhen = dvmGetRelativeTimeUsec();
                LOGD("DEX prep '%s': unzip in %dms, rewrite %dms\n",
                    fileName,
                    (int) (extractWhen - startWhen) / 1000,
                    (int) (endWhen - extractWhen) / 1000);
            }
        } else {
           ......
        }
    }

    /*
     * Map the cached version.  This immediately rewinds the fd, so it
     * doesn't have to be seeked anywhere in particular.
     */
    if (dvmDexFileOpenFromFd(fd, &pDvmDex) != 0) {
        LOGI("Unable to map %s in %s\n", kDexInJarName, fileName);
        goto bail;
    }
    ......
    LOGV("Successfully opened '%s' in '%s'\n", kDexInJarName, fileName);

    *ppJarFile = (JarFile*) calloc(1, sizeof(JarFile));
    (*ppJarFile)->archive = archive;
    (*ppJarFile)->cacheFileName = cachedName;
    (*ppJarFile)->pDvmDex = pDvmDex;
    cachedName = NULL;      // don't free it below
    result = 0;

bail:
    ......
    return result;
}
    我們主要關注dvmOpenCachedDexFile方法,首先呼叫這個方法,嘗試根據cachedName所指的優化檔名在cache中查詢並讀取優化檔案,如果沒有,則要就對Dex進行優化。我們前面說過對於PathClassLoader,在安裝階段已經生成了/data/dalvik-cache/[email protected],所以dvmOpenCachedDexFile方法返回的newFile為false。而對於DexClassLoader已經指明瞭要生成優化後的dex的路徑,這裡dvmOpenCachedDexFile方法返回的newFile為true。

    對於PathClassLoader,newFile為false,所以不需要再一次優化dex。而對於DexClassLoader,newFile為true,所以呼叫dvmOptimizeDexFile來優化dex,dvmOptimizeDexFile程式碼位於           dalvik\vm\analysis\DexPrepare.c。也就是說對於DexClassLoader也只優化一次,第二次就不會再優化了,優化還是比較耗時的。

bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLength,
    const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
{
    ......
    pid = fork();
    if (pid == 0) {
        static const char* kDexOptBin = "/bin/dexopt";
        ......
        androidRoot = getenv("ANDROID_ROOT");
        if (androidRoot == NULL) {
            LOGW("ANDROID_ROOT not set, defaulting to /system\n");
            androidRoot = "/system";
        }
        execFile = malloc(strlen(androidRoot) + strlen(kDexOptBin) + 1);
        strcpy(execFile, androidRoot);
        strcat(execFile, kDexOptBin);

		.....
        if (kUseValgrind)
            execv(kValgrinder, argv);
        else
            execv(execFile, argv);

        LOGE("execv '%s'%s failed: %s\n", execFile,
            kUseValgrind ? " [valgrind]" : "", strerror(errno));
        exit(1);
    } else {
        ......
    }
}
    也是fork出一個子執行緒去執行/system/bin/dexopt,也就是OptMain.c中的main方法,往後的執行流程請參考apk安裝和優化原理一文。簡單的脫殼程式就在dvmDexFileOpenPartial下斷點,參考使用IDA Pro進行簡單的脫殼。DexClassLoader載入dex首先要進行優化,優化的過程中會呼叫dvmDexFileOpenPartial。而dvmDexFileOpenPartial的dexAddr是dex在記憶體的基地址,dexLength是dex在記憶體中長度,詳細請參考apk安裝和優化原理一文。這樣就可以在記憶體中把dex dump出來。

    我們回到dvmJarFileOpen,繼續執行dvmDexFileOpenFromFd,程式碼位於dalvik\vm\DvmDex.c。

int dvmDexFileOpenFromFd(int fd, DvmDex** ppDvmDex)
{
    DvmDex* pDvmDex;
    DexFile* pDexFile;
    MemMapping memMap;
    int parseFlags = kDexParseDefault;
    int result = -1;

    if (gDvm.verifyDexChecksum)
        parseFlags |= kDexParseVerifyChecksum;

    if (lseek(fd, 0, SEEK_SET) < 0) {
        LOGE("lseek rewind failed\n");
        goto bail;
    }

    if (sysMapFileInShmemWritableReadOnly(fd, &memMap) != 0) {
        LOGE("Unable to map file\n");
        goto bail;
    }

    pDexFile = dexFileParse(memMap.addr, memMap.length, parseFlags);
    if (pDexFile == NULL) {
        LOGE("DEX parse failed\n");
        sysReleaseShmem(&memMap);
        goto bail;
    }

    pDvmDex = allocateAuxStructures(pDexFile);
    if (pDvmDex == NULL) {
        dexFileFree(pDexFile);
        sysReleaseShmem(&memMap);
        goto bail;
    }

    /* tuck this into the DexFile so it gets released later */
    sysCopyMap(&pDvmDex->memMap, &memMap);
    *ppDvmDex = pDvmDex;
    result = 0;

bail:
    return result;
}
    fd為優化後的dex,對於PathClassLoader,位於/data/dalvik-cache/[email protected]。對於DexClassLoader,就是在new DexClassLoader(String dexPath, String dexOutputDir, String libPath, ClassLoader parent)指定的dexOutputDir路徑中。

    在dvmDexFileOpenFromFd方法中首先呼叫sysMapFileInShmemWritableReadOnly方法把優化後的dex載入進記憶體。

int sysMapFileInShmemWritableReadOnly(int fd, MemMapping* pMap)
{
#ifdef HAVE_POSIX_FILEMAP
    off_t start;
    size_t length;
    void* memPtr;

    assert(pMap != NULL);

    if (getFileStartAndLength(fd, &start, &length) < 0)
        return -1;

    memPtr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE,
            fd, start);
    if (memPtr == MAP_FAILED) {
        LOGW("mmap(%d, R/W, FILE|PRIVATE, %d, %d) failed: %s\n", (int) length,
            fd, (int) start, strerror(errno));
        return -1;
    }
    if (mprotect(memPtr, length, PROT_READ) < 0) {
        /* this fails with EACCESS on FAT filesystems, e.g. /sdcard */
        int err = errno;
        LOGV("mprotect(%p, %d, PROT_READ) failed: %s\n",
            memPtr, length, strerror(err));
        LOGD("mprotect(RO) failed (%d), file will remain read-write\n", err);
    }

    pMap->baseAddr = pMap->addr = memPtr;
    pMap->baseLength = pMap->length = length;

    return 0;
#else
    return sysFakeMapFile(fd, pMap);
#endif
}
    程式碼位於dalvik/libdex/SysUtil.c。
    MemMapping結構體pMap中baseAddr和addr都指向dex對映到記憶體的首地址。

    返回到dvmDexFileOpenFromFd,繼續執行dexFileParse,程式碼位於dalvik\libdex\DexFile.c。

DexFile* dexFileParse(const u1* data, size_t length, int flags)
{
    DexFile* pDexFile = NULL;
    const DexHeader* pHeader;
    const u1* magic;
    int result = -1;

    if (length < sizeof(DexHeader)) {
        LOGE("too short to be a valid .dex\n");
        goto bail;      /* bad file format */
    }

    pDexFile = (DexFile*) malloc(sizeof(DexFile));
    if (pDexFile == NULL)
        goto bail;      /* alloc failure */
    memset(pDexFile, 0, sizeof(DexFile));

    /*
     * Peel off the optimized header.
     */
    if (memcmp(data, DEX_OPT_MAGIC, 4) == 0) {
        magic = data;
        if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
            LOGE("bad opt version (0x%02x %02x %02x %02x)\n",
                 magic[4], magic[5], magic[6], magic[7]);
            goto bail;
        }

        pDexFile->pOptHeader = (const DexOptHeader*) data;
        LOGV("Good opt header, DEX offset is %d, flags=0x%02x\n",
            pDexFile->pOptHeader->dexOffset, pDexFile->pOptHeader->flags);

        /* parse the optimized dex file tables */
        if (!dexParseOptData(data, length, pDexFile))
            goto bail;
		data += pDexFile->pOptHeader->dexOffset;
        length -= pDexFile->pOptHeader->dexOffset;
        ......
    }

    dexFileSetupBasicPointers(pDexFile, data);
    pHeader = pDexFile->pHeader;

    ......
    result = 0;

bail:
    if (result != 0 && pDexFile != NULL) {
        dexFileFree(pDexFile);
        pDexFile = NULL;
    }
    return pDexFile;
}

    這個方法主要是生成DexFile結構,我們首先來看DexFile結構是什麼樣子的?

typedef struct DexFile {
    /* directly-mapped "opt" header */
    const DexOptHeader* pOptHeader;

    /* pointers to directly-mapped structs and arrays in base DEX */
    const DexHeader*    pHeader;
    const DexStringId*  pStringIds;
    const DexTypeId*    pTypeIds;
    const DexFieldId*   pFieldIds;
    const DexMethodId*  pMethodIds;
    const DexProtoId*   pProtoIds;
    const DexClassDef*  pClassDefs;
    const DexLink*      pLinkData;

    /*
     * These are mapped out of the "auxillary" section, and may not be
     * included in the file.
     */
    const DexClassLookup* pClassLookup;
    const void*         pRegisterMapPool;       // RegisterMapClassPool

    /* points to start of DEX file data */
    const u1*           baseAddr;

    /* track memory overhead for auxillary structures */
    int                 overhead;

    /* additional app-specific data structures associated with the DEX */
    //void*               auxData;
} DexFile;
    程式碼位於dalvik\libdex\DexFile.h中。

    dexFileParse這個方法首先為pDexFile->pOptHeader賦值,將優化檔案頭部與DexFile資料結構下的pOptHeader變數進行關聯。

    然後呼叫dexParseOptData函式對優化資料進行處理,其作用也是將各個優化資料與DexFile資料結構中的相應成員變數進行關聯,這裡關聯的是pClassLookup和pRegisterMapPool。

    最後呼叫dexFileSetupBasicPointers將Dex檔案中其他各部分資料與DexFile資料結構建立完整的對映關係,程式碼位於dalvik\libdex\DexFile.c。

void dexFileSetupBasicPointers(DexFile* pDexFile, const u1* data) {
    DexHeader *pHeader = (DexHeader*) data;

    pDexFile->baseAddr = data;
    pDexFile->pHeader = pHeader;
    pDexFile->pStringIds = (const DexStringId*) (data + pHeader->stringIdsOff);
    pDexFile->pTypeIds = (const DexTypeId*) (data + pHeader->typeIdsOff);
    pDexFile->pFieldIds = (const DexFieldId*) (data + pHeader->fieldIdsOff);
    pDexFile->pMethodIds = (const DexMethodId*) (data + pHeader->methodIdsOff);
    pDexFile->pProtoIds = (const DexProtoId*) (data + pHeader->protoIdsOff);
    pDexFile->pClassDefs = (const DexClassDef*) (data + pHeader->classDefsOff);
    pDexFile->pLinkData = (const DexLink*) (data + pHeader->linkOff);
}
    重點說一下baseAddr指向dex檔案的頭部。

    執行完dexFileParse生成了DexFile結構,返回到dvmDexFileOpenFromFd,繼續執行allocateAuxStructures方法來生成DvmDex結構pDvmDex。程式碼位於dalvik\vm\DvmDex.c。

static DvmDex* allocateAuxStructures(DexFile* pDexFile)
{
    DvmDex* pDvmDex;
    const DexHeader* pHeader;
    u4 stringCount, classCount, methodCount, fieldCount;

    pDvmDex = (DvmDex*) calloc(1, sizeof(DvmDex));
    if (pDvmDex == NULL)
        return NULL;

    pDvmDex->pDexFile = pDexFile;
    pDvmDex->pHeader = pDexFile->pHeader;

    pHeader = pDvmDex->pHeader;

    stringCount = pHeader->stringIdsSize;
    classCount = pHeader->typeIdsSize;
    methodCount = pHeader->methodIdsSize;
    fieldCount = pHeader->fieldIdsSize;

    pDvmDex->pResStrings = (struct StringObject**)
        calloc(stringCount, sizeof(struct StringObject*));

    pDvmDex->pResClasses = (struct ClassObject**)
        calloc(classCount, sizeof(struct ClassObject*));

    pDvmDex->pResMethods = (struct Method**)
        calloc(methodCount, sizeof(struct Method*));

    pDvmDex->pResFields = (struct Field**)
        calloc(fieldCount, sizeof(struct Field*));

    LOGV("+++ DEX %p: allocateAux %d+%d+%d+%d * 4 = %d bytes\n",
        pDvmDex, stringCount, classCount, methodCount, fieldCount,
        (stringCount + classCount + methodCount + fieldCount) * 4);

    pDvmDex->pInterfaceCache = dvmAllocAtomicCache(DEX_INTERFACE_CACHE_SIZE);

    if (pDvmDex->pResStrings == NULL ||
        pDvmDex->pResClasses == NULL ||
        pDvmDex->pResMethods == NULL ||
        pDvmDex->pResFields == NULL ||
        pDvmDex->pInterfaceCache == NULL)
    {
        LOGE("Alloc failure in allocateAuxStructures\n");
        free(pDvmDex->pResStrings);
        free(pDvmDex->pResClasses);
        free(pDvmDex->pResMethods);
        free(pDvmDex->pResFields);
        free(pDvmDex);
        return NULL;
    }

    return pDvmDex;

}
    然後返回到dvmDexFileOpenFromFd,執行sysCopyMap把DvmDex結構pDvmDex的memMap附上值,最後返回DvmDex結構pDvmDex。
    

    執行完dvmDexFileOpenFromFd,返回到dvmJarFileOpen,生成了一個JarFile結構體ppJarFile,並賦值如下:

    *ppJarFile = (JarFile*) calloc(1, sizeof(JarFile));
    (*ppJarFile)->archive = archive;
    (*ppJarFile)->cacheFileName = cachedName;
    (*ppJarFile)->pDvmDex = pDvmDex;
    在返回到Dalvik_dalvik_system_DexFile_openDexFile中,生成一個DexOrJar結構體pDexOrJar,並賦值如下:
        pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
        pDexOrJar->isDex = false;
        pDexOrJar->pJarFile = pJarFile;
    然後RETURN_PTR(pDexOrJar)返回給Java層的openDexFile,並賦值給mCookie,如下:
mCookie = openDexFile(fileName, null, 0);
    下次通過mCookie,我們就能一步一步找到DexFile結構體(DexOrJar->JarFile->DvmDex->DexFile)。

    0x03

    最後附上一張DexFile的結構圖:



    圖中有一處錯誤,baseAddr和pHeader其實都指向DexHeader,也就是dex檔案頭在記憶體中的虛擬地址。

    0x03

    那麼PatchClassLoader物件是什麼時候生成的呢?

    在Android加殼原理分析一文中,在handleBindApplication方法裡需要獲取ClassLoader,這個ClassLoader物件就是PathClassLoader物件。下面來看看是怎麼形成的?

private final void handleBindApplication(AppBindData data) { 
			......
            try {
                java.lang.ClassLoader cl = instrContext.getClassLoader();
                mInstrumentation = (Instrumentation)
                    cl.loadClass(data.instrumentationName.getClassName()).newInstance();
            } catch (Exception e) {
                throw new RuntimeException(
                    "Unable to instantiate instrumentation "
                    + data.instrumentationName + ": " + e.toString(), e);
            }
			......
}
    程式碼位於frameworks\base\core\java\android\app\ActivityThread.java。

    instrContext.getClassLoader(),實現如下,程式碼位於frameworks\base\core\java\android\app\ContextImpl.java。

    @Override
    public ClassLoader getClassLoader() {
        return mPackageInfo != null ?
                mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader();
    }
    mPackageInfo.getClassLoader(),實現如下,程式碼位於frameworks\base\core\java\android\app\LoadedApk.java。
    public ClassLoader getClassLoader() {
        synchronized (this) {
            if (mClassLoader != null) {
                return mClassLoader;
            }

            if (mIncludeCode && !mPackageName.equals("android")) {
                String zip = mAppDir;

                ......
                mClassLoader =
                    ApplicationLoaders.getDefault().getClassLoader(
                        zip, mLibDir, mBaseClassLoader);
                initializeJavaContextClassLoader();
            } else {
                if (mBaseClassLoader == null) {
                    mClassLoader = ClassLoader.getSystemClassLoader();
                } else {
                    mClassLoader = mBaseClassLoader;
                }
            }
            return mClassLoader;
        }
    }
    ApplicationLoaders.getDefault().getClassLoader( zip, mLibDir, mBaseClassLoader),實現如下,程式碼位於frameworks\base\core\java\android\app\ApplicationLoaders.java中。
    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);
        }
    }
    最後的時候,我們看到了PathClassLoader物件的生成。

相關推薦

DexClassLoaderPathClassLoader載入Dex流程

    0x00    在上一篇文章apk安裝和優化原理,在最後我們分析了DexClassLoader和PathClassLoader的建構函式的不同。    PathClassLoader最後呼叫的是new DexFile(pathFile),而DexClassLoader

DexClassLoaderPathClassLoader載入機制

分析 sbin 分享 return bject _id ise 否則 nts 0x00 在DexClassLoader和PathClassLoader載入Dex流程一文中,我們分析了dex文件怎樣形成了DexFile結構體。本文中解說類載入機制,實際上就是生

關於DexClassLoaderPathClassLoader,以及Dalvik載入類的過程

android中,dalvik虛擬機器載入的是dex檔案,用於載入類的ClassLoader是PathClassLoader和DexClassLoader。PathClassLoader和DexClassLoader都繼承自BaseDexClassLoader,

【Android高階】DexClassloaderPathClassloader動態載入外掛的實現

(一)DexClassloader 一、基本概念:          在Android中可以跟java一樣實現動態載入jar,但是Android使用德海Dalvik VM,不能直接載入java打包jar的byte code,需要通過dx工具來優化Dalvik byte

DexClassLoader PathClassLoader簡單記錄

DexClassLoader 和 PathClassLoader 在Android中,ClassLoader是一個抽象類,實際開發過程中,我們一般是使用其具體的子類DexClassLoader、PathClassLoader這些類載入器來載入類的,它們的不同之處是: DexClas

利用DexClassLoader動態載入dex檔案

Java中也有類載入器ClassLoader,其作用是動態裝載Class檔案,當我們從網路下載Class檔案,或者在編譯時不參與而在執行時動態呼叫時就需要用類載入器。由於Android對class檔案進行了重新打包和優化,最終APK檔案中包含的是dex檔案,載入這種檔案就需

cognos安裝配置即席報表流程

自帶jdk pan 慢慢 成對 spa 無法 加密文件 用戶權限 退出 安裝前的配置: 1、 Cognos數據庫的創建和用戶的創建 註意:字符集需要設置為UTF-8;Cognos用戶權限可以給dba; 2、系統上原有JDK的刪除(因為Cognos已經自帶JDK) 安裝

uefi BIOSlegacy BIOS 啟動流程區別

necessary gac table sin graphic pow drive con for Under BIOS 1. System switched on - Power-on self-test or POST process 2. After PO

軟件工程軟件開發流程

engine 有一種 pos 模式 mode 統一 sil soft ctu 人們在開發、運營、維護軟件的過程中有很多技術、做法、習慣和思想體系。軟件工程把這些相關的技術和過程統一到一個體系中,叫“軟件開發流程”。 軟件開發流程的目的是為了提高軟件開發、運營、維護的效率,並

[譯]Android view 測量布局繪制的流程

註意 images draw can www -i str 中一 opengl-es 原文鏈接 創造優秀的用戶體驗是我們開發者的主要目標之一.為此, 我們首先要了解系統是如何工作的, 這樣我們才可以更好的與系統配合, 從它的優點中獲益, 規避它的缺陷.

Oracle_RAC宕機hang分析處理流程

reply 事件分析 宕機 等待 nal 故障 zab ali 手動 目的:分享一下公司的db故障處理流程,主要是思想。事件描述及影響:2018年9月30日04:43點,zabbix告警odsdb2數據庫疑似宕機,機房值班人員通過堡壘機無法登錄數據庫服務器,從其他機器也無法

關於apk加殼之動態載入dex檔案

由於自己之前做了一個關於手機令牌的APK軟體,在實現的過程中儘管使用了native so進行一定的邏輯演算法保護,但是在自己逆向破解的過程中發現我的手機令牌關鍵資料能夠“輕易地”暴露出來,所以我就想進一步的對其進行加固。於是,我使用的網上常用的梆梆加固、愛加密和阿里的聚安全應用來對我的apk進行一個

Flutter ListView 列表點選網頁載入

上一篇講了使用ListView載入列表資料,本篇,我們講下列表項的點選,因為本篇的例子點選後是載入一個網頁,所以本篇也講下類似於Android的WebView和iOS的UIWebView載入網頁。效果如下: item點選 在Android中,您可以通過呼叫方法setOnClic

軟體分類軟體測試工作流程

軟體的分類  其中,系統軟體指的是和計算機硬體緊密配合在一起的,使得計算機系統的各個部件、相關的軟體和資料協調、高效工作的軟體。例如,作業系統,資料庫管理系統等。 支撐軟體指的是協助使用者開發軟體產品的工具。 應用軟體是在特定領域開發,為特定目的服務的一類軟體。

PHP設計模式:類自動載入、PSR-0規範、鏈式操作、11種面向物件設計模式實現使用、OOP的基本原則自動載入配置

一、類自動載入      SPL函式 (standard php librarys)      類自動載入,儘管 __autoload() 函式也能自動載入類和介面,但更建議使用&nbs

WWW實現圖片資源顯示以及儲存本地載入

WWW實現圖片資源顯示以及儲存和本地載入 using UnityEngine; using System.Collections; using System.IO; using UnityEditor; enum GetPicType { DownLoad = 0, Local

Java類載入器( CLassLoader ) 死磕8: 使用ASM,載入器實現AOP

【正文】Java類載入器(  CLassLoader ) 死磕8:  使用ASM,和類載入器實現AOP 本小節目錄 8.1. ASM位元組碼操作框架簡介 8.2. ASM和訪問者模式 8.3. 用於增強位元組碼的事務類 8.4 通過ASM訪問註解 8.5. 通過ASM注入AOP事務程式

Tomcat熱部署載入的方法

想從新啟動伺服器(伺服器從新啟動花時間),想直接獲得(debug)結果.有兩種方式熱部署 和熱載入: 熱載入 在server.xml -> context 屬性中 設定 reloadable=“true” 熱部署 在server.xml -> context

使用jquery-weui製作的下拉重新整理滾動載入

一、前期檔案中引入weui.min.css,jquery-weui.min.css,jquery-2.1.4.js,jquery-weui.min.js,fastclick.js這些檔案。 二、html程式碼 <head> <meta charset="utf-8

View的學習筆記(三)_自己造輪子_一個帶header重新整理頭footer載入腳的

帶重新整理指示item的RecyclerView 實現效果 造輪子的經驗總結 設計思路 事件分發處理 實現效果 使用方法 可以指定控制元件大小,預設的RecyclerView會填充指定的大小 自定義屬性就