淺析dex文件加載機制


我們可以利用DexClassLoader來實現動態加載dex文件,而很多資料也只是對於DexClassLoader的使用進行了介紹,沒有深入講解dex的動態加載機制,我們就借助於Android4.4的源碼來探索。先從一個簡單的動態加載dex文件開始 具體實現細節可以參考這篇文章AndroidDex數據動態加載技術

Android4.4的源碼在百度網盤分享: Android 4.4源碼下載

先是我們要封裝到text.jar文件中的很簡單的調用函數,只是簡單的產生Toast:

/*
 * 對外接口
 */
public interface Iinterface {
    public void call();
    public String getData();
    
}
public class IClass implements Iinterface{
    private Context context;    
    public IClass(Context context){
        super();
        this.context = context;
    }
    //@Override
    public void call() {
        // TODO Auto-generated method stub
        Toast.makeText(context, "call method", 0).show();
    }
    //@Override
    public String getData() {
        // TODO Auto-generated method stub
        return "Hello ,I am from IClass";
    }    
}

在MainActivity中只是解壓test.jar文件,然后通過DexClassLoader類來加載dex文件,最后通過反射調用相關方法:

public class FileUtile {
    //MainActivity  "testdex.jar", "testdex.jar"
    public static void CopyAssertJarToFile(Context context, String filename,
            String des) {
        try {
            //返回 File ,獲取外部存儲目錄即 SDCard
            //path    "/mnt/sdcard/testdex.jar" 
            //File.separator  Windows \   linux /
            File file = new File(Environment.getExternalStorageDirectory().getPath()
                    + File.separator + des);
            if (file.exists()) {
                return;
            }
            //取得資源文件的輸入流
            InputStream inputStream = context.getAssets().open(filename);
            file.createNewFile(); //創建"/mnt/sdcard/testdex.jar" 文件
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            byte buffer[] = new byte[1024];
            int len = 0;
            while ((len = inputStream.read(buffer)) != 0) {
                fileOutputStream.write(buffer, 0, len);
            }
            inputStream.close();
            fileOutputStream.close();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}



public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);    
        FileUtile.CopyAssertJarToFile(this, "testdex.jar", "testdex.jar");      
        /*拷貝*/
        File file = new File(Environment.getExternalStorageDirectory()
                .toString() + File.separator + "testdex.jar");      
        final File optimizedDexOutputPath = getDir("temp", Context.MODE_PRIVATE);
        /*
         * Parameters
dexPath    需要裝載的APK或者Jar文件的路徑。包含多個路徑用File.pathSeparator間隔開,在Android上默認是 ":" 
optimizedDirectory    優化后的dex文件存放目錄,不能為null
libraryPath    目標類中使用的C/C++庫的列表,每個目錄用File.pathSeparator間隔開; 可以為 null
parent    該類裝載器的父裝載器,一般用當前執行類的裝載器
         */
        DexClassLoader classLoader = new DexClassLoader(file.getAbsolutePath(),
                optimizedDexOutputPath.getAbsolutePath(), null,
                getClassLoader()); 
        try {
            Class<?> iclass = classLoader.loadClass("com.demo.dex.IClass");
            Constructor<?> istructor = iclass.getConstructor(Context.class);
            //利用反射原理去調用
            Method method = iclass.getMethod("call", null);
            String data = (String) method.invoke(istructor.newInstance(this), null);
            //System.out.println(data);
            Log.d("CCDebug",data);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }    
}

我們從DexClassLoaderl類開始分析:

在\libcore\dalvik\src\main\java\dalvik\system\ DexClassLoader.java文件下

public class DexClassLoader extends BaseDexClassLoader {
    public DexClassLoader(String dexPath, String optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        super(dexPath, new File(optimizedDirectory), libraryPath, parent);
    }
}

非常簡單的DexClassLoader的構造函數,只是調用了父類BaseDexClassLoader的構造函數,在同一目錄下的BaseDexClassLoader.java的源碼:

public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        super(parent);
        this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
}

同樣的,也是很簡單的調用父類ClassLoader的構造函數,然后生成一個DexPathList對象,在同一目錄下的DexPathList.java文件中:

public DexPathList(ClassLoader definingContext, String dexPath,
            String libraryPath, File optimizedDirectory) {
    //省略參數校驗以及異常處理的代碼
        this.definingContext = definingContext;
        ……
        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
                                           suppressedExceptions);
        ……
        this.nativeLibraryDirectories = splitLibraryPath(libraryPath);
}

我們繼續閱讀DexPathList.java文件中makeDexElements 的關鍵代碼:

private static Element[] makeDexElements(ArrayList<File> files, File optimizedDirectory,
                                             ArrayList<IOException> suppressedExceptions) {
     // ……
        for (File file : files) {
            File zip = null;
            DexFile dex = null;
            String name = file.getName();
            if (name.endsWith(DEX_SUFFIX)) {   //.dex文件
                // Raw dex file (not inside a zip/jar).
                try {
                    dex = loadDexFile(file, optimizedDirectory);
                } catch (IOException ex) {
                    System.logE("Unable to load dex file: " + file, ex);
                }
            } else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX)
                    || name.endsWith(ZIP_SUFFIX)) {
                    //.apk  .jar  .zip文件
                zip = file;
                try {
                    dex = loadDexFile(file, optimizedDirectory);
                } catch (IOException suppressed) {
                    suppressedExceptions.add(suppressed);
                }
            } else if (file.isDirectory()) {
                // We support directories for looking up resources.
                // This is only useful for running libcore tests.
                elements.add(new Element(file, true, null, null));
            } else {
                System.logW("Unknown file type for: " + file);
            }
        }
                    //……
        return elements.toArray(new Element[elements.size()]);
    }

DexPathList.java文件中:

private static DexFile loadDexFile(File file, File optimizedDirectory)
            throws IOException {
        if (optimizedDirectory == null) {
            return new DexFile(file);
        } else {
            String optimizedPath = optimizedPathFor(file, optimizedDirectory);
            return DexFile.loadDex(file.getPath(), optimizedPath, 0);
        }
    }
//生成odex的目錄 private static String optimizedPathFor(File path, File optimizedDirectory) { String fileName = path.getName(); if (!fileName.endsWith(DEX_SUFFIX)) { int lastDot = fileName.lastIndexOf("."); if (lastDot < 0) { fileName += DEX_SUFFIX; } else { StringBuilder sb = new StringBuilder(lastDot + 4); sb.append(fileName, 0, lastDot); sb.append(DEX_SUFFIX); fileName = sb.toString(); } } File result = new File(optimizedDirectory, fileName); return result.getPath(); }

optimizedPathFor主要是對文件的后綴進行修正,如果沒有后綴名,就在末尾加上.dex,如果文件結尾不是.dex,就將后綴替換為.dex,然后創建我們的.dex文件,然后返回我們創建的.dex文件的路徑,繼續執行DexFile.loadDex() 函數:

static public DexFile loadDex(String sourcePathName, String outputPathName,
        int flags) throws IOException {
        return new DexFile(sourcePathName, outputPathName, flags);
}

這里直接返回了一個DexFile對象,下面來看看這個類的構造函數:

//sourceName  就是我們要加載的自己的.jar文件路徑
// outputName 在optimizedPathFor() 函數中修正的加載.dex的路徑
private DexFile(String sourceName, String outputName, int flags) throws IOException {
        if (outputName != null) {
            try {
                String parent = new File(outputName).getParent();
        /* ??????*/
                if (Libcore.os.getuid() != Libcore.os.stat(parent).st_uid) {
                    throw new IllegalArgumentException("Optimized data directory " + parent
                            + " is not owned by the current user. Shared storage cannot protect"
                            + " your application from code injection attacks.");
                }
            } catch (ErrnoException ignored) {
                // assume we'll fail with a more contextual error later
            }
        }
        //我們的重點就是在openDexFile()函數上
        mCookie = openDexFile(sourceName, outputName, flags);
        mFileName = sourceName;
        guard.open("close");
        //System.out.println("DEX FILE cookie is " + mCookie);
}

openDexFile函數的返回值是一個整型,保存在mCookie中,文件名保存在mFileName中

private static int openDexFile(String sourceName, String outputName,
        int flags) throws IOException {
        return openDexFileNative(new File(sourceName).getCanonicalPath(),
                                 (outputName == null) ? null : new File(outputName).getCanonicalPath(),
                                 flags);
}

在openDexFile()中只是調用了openDexFileNative () 繼續跟入在\ dalvik\v m\nat ive\dalvik _sys tem_DexFile.cpp文件中的openDexFileNative() 函數,接下重點就在這個函數:

static void Dalvik_dalvik_system_DexFile_openDexFileNative(const u4* args,
    JValue* pResult)
{
//args[0]: sourceName java層傳入的
//args[1]: outputName    
    StringObject* sourceNameObj = (StringObject*) args[0];
    StringObject* outputNameObj = (StringObject*) args[1];
    DexOrJar* pDexOrJar = NULL;
    JarFile* pJarFile;
RawDexFile* pRawDexFile;
//DexOrJar*  JarFile*   RawDexFile* 目錄
    char* sourceName;
    char* outputName;
    //……
    sourceName = dvmCreateCstrFromString(sourceNameObj);
    if (outputNameObj != NULL)
        outputName = dvmCreateCstrFromString(outputNameObj);
    else
        outputName = NULL;
/*判斷要加載的dex是否為系統中的dex文件
* gDvm ???
*/
    if (dvmClassPathContains(gDvm.bootClassPath, sourceName)) {
        ALOGW("Refusing to reopen boot DEX '%s'", sourceName);
        dvmThrowIOException(
            "Re-opening BOOTCLASSPATH DEX files is not allowed");
        free(sourceName);
        free(outputName);
        RETURN_VOID();
    }

    /*
     * Try to open it directly as a DEX if the name ends with ".dex".
     * If that fails (or isn't tried in the first place), try it as a
     * Zip with a "classes.dex" inside.
     */
    //判斷sourcename擴展名是否是.dex
    if (hasDexExtension(sourceName)
            && dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) {
        ALOGV("Opening DEX file '%s' (DEX)", sourceName);
        pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
        pDexOrJar->isDex = true;
        pDexOrJar->pRawDexFile = pRawDexFile;
        pDexOrJar->pDexMemory = NULL;
    //.jar文件
    } else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) {
        ALOGV("Opening DEX file '%s' (Jar)", sourceName);
        pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
        pDexOrJar->isDex = false;
        pDexOrJar->pJarFile = pJarFile;
        pDexOrJar->pDexMemory = NULL;
} else {
//都不滿足,拋出異常
        ALOGV("Unable to open DEX file '%s'", sourceName);
        dvmThrowIOException("unable to open DEX file");
    }
if (pDexOrJar != NULL) {
        pDexOrJar->fileName = sourceName;
    //把pDexOr這個結構體中的內容加到gDvm中的userDexFile結構的hash表中,便於Dalvik以后的查找
        addToDexFileTable(pDexOrJar);
    } else {
        free(sourceName);
    }
    free(outputName);
    RETURN_PTR(pDexOrJar);
}

接下來再看對.dex文件的處理函數dvmRawDexFileOpen 在dalvik\vm\RawDexFile.cpp文件中:

/* See documentation comment in header. */
int dvmRawDexFileOpen(const char* fileName, const char* odexOutputName,
    RawDexFile** ppRawDexFile, bool isBootstrap)
{
    DvmDex* pDvmDex = NULL;
    char* cachedName = NULL;
    int result = -1;
    int dexFd = -1;
    int optFd = -1;
    u4 modTime = 0;
    u4 adler32 = 0;
    size_t fileSize = 0;
    bool newFile = false;
    bool locked = false;
    dexFd = open(fileName, O_RDONLY);  //打開dex文件
    if (dexFd < 0) goto bail;
    /* If we fork/exec into dexopt, don't let it inherit the open fd. */
dvmSetCloseOnExec(dexFd);//dexfd不繼承
//校驗dex文件的標志,將第8字節開始的4個字節賦值給adler32。
    if (verifyMagicAndGetAdler32(dexFd, &adler32) < 0) {
        ALOGE("Error with header for %s", fileName);
        goto bail;
    }
    //得到dex文件的大小和修改時間,保存在modTime和filesize中
    if (getModTimeAndSize(dexFd, &modTime, &fileSize) < 0) {
        ALOGE("Error with stat for %s", fileName);
        goto bail;
    }

    //odexOutputName就是odex文件名,如果odexOutputName為空,則自動生成一個。
    if (odexOutputName == NULL) {
        cachedName = dexOptGenerateCacheFileName(fileName, NULL);
        if (cachedName == NULL)
            goto bail;
    } else {
        cachedName = strdup(odexOutputName);   
    }
    //主要是驗證緩存文件名的正確性,之后將dexOptHeader結構寫入fd中
    optFd = dvmOpenCachedDexFile(fileName, cachedName, modTime,
        adler32, isBootstrap, &newFile, /*createIfMissing=*/true);
    locked = true;
  
    if (newFile) {
        u8 startWhen, copyWhen, endWhen;
        bool result;
        off_t dexOffset;
        dexOffset = lseek(optFd, 0, SEEK_CUR);  //文件指針的位置
        result = (dexOffset > 0);
        if (result) {
            startWhen = dvmGetRelativeTimeUsec();
    //將dex文件中的內容拷貝到當前odex文件,也就是dexOffset開始
            result = copyFileToFile(optFd, dexFd, fileSize) == 0;
            copyWhen = dvmGetRelativeTimeUsec();
        }
        if (result) {
    //優化odex文件
            result = dvmOptimizeDexFile(optFd, dexOffset, fileSize,
                fileName, modTime, adler32, isBootstrap);
        }
    }
    /*
     * Map the cached version.  This immediately rewinds the fd, so it
     * doesn't have to be seeked anywhere in particular.
     */
//將odex文件映射到內存空間(mmap),並用mprotect將屬性置為只讀屬性,並將映射的dex結構放在pDvmDex數據結構中,具體代碼在下面。
    if (dvmDexFileOpenFromFd(optFd, &pDvmDex) != 0) {
        ALOGI("Unable to map cached %s", fileName);
        goto bail;
    }
……
}

 

 

//Dalvik/vm/RewDexFile.cpp
static int verifyMagicAndGetAdler32(int fd, u4 *adler32)
{
    u1 headerStart[12];
    ssize_t amt = read(fd, headerStart, sizeof(headerStart));
    if (amt < 0) {
        ALOGE("Unable to read header: %s", strerror(errno));
        return -1;
    }
    if (amt != sizeof(headerStart)) {
        ALOGE("Unable to read full header (only got %d bytes)", (int) amt);
        return -1;
    }
    if (!dexHasValidMagic((DexHeader*) (void*) headerStart)) {
        return -1;
    }
    *adler32 = (u4) headerStart[8]
        | (((u4) headerStart[9]) << 8)
        | (((u4) headerStart[10]) << 16)
        | (((u4) headerStart[11]) << 24);

    return 0;
}

 

 

//dalvik\vm\DvmDex.cpp
/*
 * Given an open optimized DEX file, map it into read-only shared memory and
 * parse the contents.
 *
 * Returns nonzero on error.
 */
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) {
        ALOGE("lseek rewind failed");
        goto bail;
    }
    //mmap映射fd文件,就是我們之前的odex文件
   if (sysMapFileInShmemWritableReadOnly(fd, &memMap) != 0) {
        ALOGE("Unable to map file");
        goto bail;
    }
    pDexFile = dexFileParse((u1*)memMap.addr, memMap.length, parseFlags);
    if (pDexFile == NULL) {
        ALOGE("DEX parse failed");
        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 */
//將映射odex文件的內存拷貝到DvmDex的結構中
    sysCopyMap(&pDvmDex->memMap, &memMap);
    pDvmDex->isMappedReadOnly = true;
    *ppDvmDex = pDvmDex;
    result = 0;

bail:
    return result;
}


/*dalvik\libdex\SysUtil.cpp
*/
int sysMapFileInShmemWritableReadOnly(int fd, MemMapping* pMap)
{
    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 (mprotect(memPtr, length, PROT_READ) < 0) {
      //…….
    }
    pMap->baseAddr = pMap->addr = memPtr;
    pMap->baseLength = pMap->length = length;
return 0;
//……
}

下面在分析文件后綴不是.dex的情況:

/*如果不是.dex文件*/
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;

//打開.jar文件並映射,內存結構放在ZipArchive中,之后將具體分析的代碼
    if (dexZipOpenArchive(fileName, &archive) != 0)
        goto bail;
    archiveOpen = true;
dvmSetCloseOnExec(dexZipGetArchiveFd(&archive));  //不繼承

    // openAlternateSuffix函數將fileName的后綴名改為”.odex”,例如
    //”Hello.jar”--”Hello.odex”,然后調用open()”打開”Hello.odex文件
    //如果成功返回”Hello.odex”的文件描述符
    fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName);
    if (fd >= 0) {
        ALOGV("Using alternate file (odex) for %s ...", fileName);
        //…檢驗optHeader 
        if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) {
            //……
            goto tryArchive;
        } 
    } else {  
        ZipEntry entry;
tryArchive:
        /*
         * Pre-created .odex absent or stale.  Look inside the jar for a
         * "classes.dex".
         */
// static const char* kDexInJarName = "classes.dex";
        /*
            在dexZipFindEntry函數中,對kDexInJarName也就是”class.dex”進行hash運算,找到”class.dex”在archive結構中的表項
        */
entry = dexZipFindEntry(&archive, kDexInJarName);
        if (entry != NULL) {
            bool newFile = false;
           
        //如果odex緩存路徑為空,則自動生成一個路徑
            if (odexOutputName == NULL) {
                cachedName = dexOptGenerateCacheFileName(fileName,
                                kDexInJarName);
                if (cachedName == NULL)
                    goto bail;
            } else {
                cachedName = strdup(odexOutputName);
            }
             //創建cachedName對應的文件  (.odex)
            fd = dvmOpenCachedDexFile(fileName, cachedName,
                    dexGetZipEntryModTime(&archive, entry),
                    dexGetZipEntryCrc32(&archive, entry),
            //……
            locked = true;
            //……
            if (newFile) {   //成功創建.odex文件
                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) {
                    //優化dex文件-.odex
                    result = dvmOptimizeDexFile(fd, dexOffset,
                                dexGetZipEntryUncompLen(&archive, entry),
                                fileName,
                                dexGetZipEntryModTime(&archive, entry),
                                dexGetZipEntryCrc32(&archive, entry),
                                isBootstrap);
                }
//已經得到了.odex文件,下面的流程就和.dex文件一樣了。
    //映射.odex文件,
    if (dvmDexFileOpenFromFd(fd, &pDvmDex) != 0)   
    //…………
    return result;
}

 

 

//\dalvik\libdex\SysUtil.cpp
int dexZipOpenArchive(const char* fileName, ZipArchive* pArchive)
{
    int fd, err;
    …….
    memset(pArchive, 0, sizeof(ZipArchive));
    //打開文件
    fd = open(fileName, O_RDONLY | O_BINARY, 0);
      ……
    return dexZipPrepArchive(fd, fileName, pArchive);
}


int dexZipPrepArchive(int fd, const char* debugFileName, ZipArchive* pArchive)
{
    int result = -1;
    memset(pArchive, 0, sizeof(*pArchive));
    pArchive->mFd = fd;   //Zip的文件描述符
    if (mapCentralDirectory(fd, debugFileName, pArchive) != 0)
        goto bail;
    if (parseZipArchive(pArchive) != 0) { 
        goto bail;
    }
    /* success */
    result = 0;
bail:
    if (result != 0)
        dexZipCloseArchive(pArchive); //失敗釋放pArchive結構
    return result;
}


static int mapCentralDirectory(int fd, const char* debugFileName,
    ZipArchive* pArchive)
{
    /*
     * Get and test file length.
     */
//檢驗文件長度的有效性
    off64_t fileLength = lseek64(fd, 0, SEEK_END);
    if (fileLength < kEOCDLen) {
             return -1;
    }  
    size_t readAmount = kMaxEOCDSearch;
    if (fileLength < off_t(readAmount))
        readAmount = fileLength;

    u1* scanBuf = (u1*) malloc(readAmount);
    if (scanBuf == NULL) {
        return -1;
    }
    int result = mapCentralDirectory0(fd, debugFileName, pArchive,
            fileLength, readAmount, scanBuf);
    free(scanBuf);
    return result;
}


tatic int mapCentralDirectory0(int fd, const char* debugFileName,
        ZipArchive* pArchive, off64_t fileLength, size_t readAmount, u1* scanBuf)
{
    /*
     * Make sure this is a Zip archive.
     */
//校驗文件是否合法的Zip文件
    //……                                //偏移16的地方  //偏移12
    if (sysMapFileSegmentInShmem(fd, centralDirOffset, centralDirSize,
            &pArchive->mDirectoryMap) != 0)
    {
        ALOGW("Zip: cd map failed");
        return -1;
    }
    pArchive->mNumEntries = numEntries;
    pArchive->mDirectoryOffset = centralDirOffset;
    return 0;
}


int sysMapFileSegmentInShmem(int fd, off_t start, size_t length,
    MemMapping* pMap)
{
    size_t actualLength;
    off_t actualStart;
    int adjust;
    void* memPtr;
    assert(pMap != NULL);
    /* adjust to be page-aligned */
    adjust = start % SYSTEM_PAGE_SIZE;
    actualStart = start - adjust;
    actualLength = length + adjust;
    //映射
    memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED,
                fd, actualStart);
       // …….
    pMap->baseAddr = memPtr;
    pMap->baseLength = actualLength;
    pMap->addr = (char*)memPtr + adjust;
    pMap->length = length;
    return 0;
}

ZipArchive的結構體如下:

struct ZipArchive {
    /* open Zip archive */
    int         mFd;   //打開的zip文件
    /* mapped central directory area */
    off_t       mDirectoryOffset;   
    MemMapping  mDirectoryMap;   //映射內存的結構
    /* number of entries in the Zip archive */
    int         mNumEntries;      //
    int         mHashTableSize;   //名字hash表的大小
    ZipHashEntry* mHashTable;     //hash表的表項,
};


struct ZipHashEntry {
    const char*     name;
    unsigned short   nameLen;
};

 

 

我們可以簡要總結下整個的加載流程,首先是對文件名的修正,后綴名置為”.dex”作為輸出文件,然后生個一個DexPathList對象函數直接返回一個DexPathList對象,

在DexPathList的構造函數中調用makeDexElements()函數,在makeDexElement()函數中調用loadDexFile()開始對.dex或者是.jar .zip .apk文件進行處理,

跟入loadDexFile()函數中,會發現里面做的工作很簡單,調用optimizedPathFor()函數對optimizedDiretcory路徑進行修正。

之后才真正通過DexFile.loadDex()開始加載文件中的數據,其中的加載也只是返回一個DexFile對象。

在DexFile類的構造函數中,重點便放在了其調用的openDexFile()函數,在openDexFile()中調用了openDexFileNative()真正進入native層,

在openDexFileNative()的真正實現中,對於后綴名為.dex的文件或者其他文件(.jar .apk .zip)分開進行處理:

.dex文件調用dvmRawDexFileOpen();
其他文件調用dvmJarFileOpen()。

在dvmRawDexFileOpen()函數中,檢驗dex文件的標志,檢驗odex文件的緩存名稱,之后將dex文件拷貝到odex文件中,並對odex進行優化

調用dvmDexFileOpenFromFd()對優化后的odex文件進行映射,通過mprotect置為"只讀"屬性並將映射的內存結構保存在DvmDex*結構中。

dvmJarFileOpen()先對文件進行映射,結構保存在ZipArchive中,然后再嘗試以文件名作為dex文件名來“打開”文件,
如果失敗,則調用dexZipFindEntry在ZipArchive的名稱hash表中找名為"class.dex"的文件,然后創建odex文件,下面就和
dvmRawDexFileOpen()一樣了,就是對dex文件進行優化和映射。

也只是分析了一個大概流程,還有很多有待之后進行深入。而這里對於閱讀Android源碼,有了新的體會,首先是工具上,我之前一直是用Source InSight 但是對於一些函數的實現,找起來卻是不太方便,因為必須要將函數實現的文件導入到工程中,而用VS來閱讀源碼,利用Ctrl+Shift+F的功能,在Android源碼目錄下搜索更為方便,然后可以在Source InSight中進行導入,閱讀。其次不得不說閱讀源碼真的是一個比較痛苦的過程,但真的學習下來,收獲還是很大的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM