zoukankan      html  css  js  c++  java
  • 浅析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类开始分析:

    在libcoredalviksrcmainjavadalviksystem 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 () 继续跟入在 dalvikv m at ivedalvik _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 在dalvikvmRawDexFile.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;
    }
    //dalvikvmDvmDex.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;
    }
    
    
    /*dalviklibdexSysUtil.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;
    }
    //dalviklibdexSysUtil.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中进行导入,阅读。其次不得不说阅读源码真的是一个比较痛苦的过程,但真的学习下来,收获还是很大的。

  • 相关阅读:
    UnicodeDecodeError: 'utf-8' codec can't decode byte 0xef in position 99: invalid continuation byte
    Java的内存机制
    sort
    ajax()函数传值中文乱码解决方法介绍
    三种实现Ajax的方式
    找到div下的第一个ul
    POI 单元格
    js对字符串进行编码方法总结
    相对路径和绝对路径的区别,java获取项目访问路径的方法
    sql存储过程几个简单例子
  • 原文地址:https://www.cnblogs.com/lanrenxinxin/p/4712224.html
Copyright © 2011-2022 走看看