我们可以利用DexClassLoader来实现动态加载dex文件,而很多资料也只是对于DexClassLoader的使用进行了介绍,没有深入讲解dex的动态加载机制,我们就借助于Android4.4的源码来探索。先从一个简单的动态加载dex文件开始 具体实现细节可以参考这篇文章AndroidDex数据动态加载技术

Android4.4的源码在百度网盘分享: Android 4.4源码下载

先是我们要封装到text.jar文件中的很简单的调用函数,只是简单的产生Toast:

  1. /*
  2. * 对外接口
  3. */
  4. public interface Iinterface {
  5. public void call();
  6. public String getData();
  7.  
  8. }
  1. public class IClass implements Iinterface{
  2. private Context context;
  3. public IClass(Context context){
  4. super();
  5. this.context = context;
  6. }
  7. //@Override
  8. public void call() {
  9. // TODO Auto-generated method stub
  10. Toast.makeText(context, "call method", 0).show();
  11. }
  12. //@Override
  13. public String getData() {
  14. // TODO Auto-generated method stub
  15. return "Hello ,I am from IClass";
  16. }
  17. }

在MainActivity中只是解压test.jar文件,然后通过DexClassLoader类来加载dex文件,最后通过反射调用相关方法:

  1. public class FileUtile {
  2. //MainActivity "testdex.jar", "testdex.jar"
  3. public static void CopyAssertJarToFile(Context context, String filename,
  4. String des) {
  5. try {
  6. //返回 File ,获取外部存储目录即 SDCard
  7. //path "/mnt/sdcard/testdex.jar"
  8. //File.separator Windows \ linux /
  9. File file = new File(Environment.getExternalStorageDirectory().getPath()
  10. + File.separator + des);
  11. if (file.exists()) {
  12. return;
  13. }
  14. //取得资源文件的输入流
  15. InputStream inputStream = context.getAssets().open(filename);
  16. file.createNewFile(); //创建"/mnt/sdcard/testdex.jar" 文件
  17. FileOutputStream fileOutputStream = new FileOutputStream(file);
  18. byte buffer[] = new byte[1024];
  19. int len = 0;
  20. while ((len = inputStream.read(buffer)) != 0) {
  21. fileOutputStream.write(buffer, 0, len);
  22. }
  23. inputStream.close();
  24. fileOutputStream.close();
  25. } catch (Exception e) {
  26. // TODO Auto-generated catch block
  27. e.printStackTrace();
  28. }
  29. }
  30. }
  31.  
  32. public class MainActivity extends Activity {
  33. @Override
  34. protected void onCreate(Bundle savedInstanceState) {
  35. super.onCreate(savedInstanceState);
  36. setContentView(R.layout.activity_main);
  37. FileUtile.CopyAssertJarToFile(this, "testdex.jar", "testdex.jar");
  38. /*拷贝*/
  39. File file = new File(Environment.getExternalStorageDirectory()
  40. .toString() + File.separator + "testdex.jar");
  41. final File optimizedDexOutputPath = getDir("temp", Context.MODE_PRIVATE);
  42. /*
  43. * Parameters
  44. dexPath 需要装载的APK或者Jar文件的路径。包含多个路径用File.pathSeparator间隔开,在Android上默认是 ":"
  45. optimizedDirectory 优化后的dex文件存放目录,不能为null
  46. libraryPath 目标类中使用的C/C++库的列表,每个目录用File.pathSeparator间隔开; 可以为 null
  47. parent 该类装载器的父装载器,一般用当前执行类的装载器
  48. */
  49. DexClassLoader classLoader = new DexClassLoader(file.getAbsolutePath(),
  50. optimizedDexOutputPath.getAbsolutePath(), null,
  51. getClassLoader());
  52. try {
  53. Class<?> iclass = classLoader.loadClass("com.demo.dex.IClass");
  54. Constructor<?> istructor = iclass.getConstructor(Context.class);
  55. //利用反射原理去调用
  56. Method method = iclass.getMethod("call", null);
  57. String data = (String) method.invoke(istructor.newInstance(this), null);
  58. //System.out.println(data);
  59. Log.d("CCDebug",data);
  60. } catch (Exception e) {
  61. // TODO Auto-generated catch block
  62. e.printStackTrace();
  63. }
  64. }
  65. }

我们从DexClassLoaderl类开始分析:

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

  1. public class DexClassLoader extends BaseDexClassLoader {
  2. public DexClassLoader(String dexPath, String optimizedDirectory,
  3. String libraryPath, ClassLoader parent) {
  4. super(dexPath, new File(optimizedDirectory), libraryPath, parent);
  5. }
  6. }

非常简单的DexClassLoader的构造函数,只是调用了父类BaseDexClassLoader的构造函数,在同一目录下的BaseDexClassLoader.java的源码:

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

同样的,也是很简单的调用父类ClassLoader的构造函数,然后生成一个DexPathList对象,在同一目录下的DexPathList.java文件中:

  1. public DexPathList(ClassLoader definingContext, String dexPath,
  2. String libraryPath, File optimizedDirectory) {
  3. //省略参数校验以及异常处理的代码
  4. this.definingContext = definingContext;
  5. ……
  6. this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
  7. suppressedExceptions);
  8. ……
  9. this.nativeLibraryDirectories = splitLibraryPath(libraryPath);
  10. }

我们继续阅读DexPathList.java文件中makeDexElements 的关键代码:

  1. private static Element[] makeDexElements(ArrayList<File> files, File optimizedDirectory,
  2. ArrayList<IOException> suppressedExceptions) {
  3. // ……
  4. for (File file : files) {
  5. File zip = null;
  6. DexFile dex = null;
  7. String name = file.getName();
  8. if (name.endsWith(DEX_SUFFIX)) { //.dex文件
  9. // Raw dex file (not inside a zip/jar).
  10. try {
  11. dex = loadDexFile(file, optimizedDirectory);
  12. } catch (IOException ex) {
  13. System.logE("Unable to load dex file: " + file, ex);
  14. }
  15. } else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX)
  16. || name.endsWith(ZIP_SUFFIX)) {
  17. //.apk .jar .zip文件
  18. zip = file;
  19. try {
  20. dex = loadDexFile(file, optimizedDirectory);
  21. } catch (IOException suppressed) {
  22. suppressedExceptions.add(suppressed);
  23. }
  24. } else if (file.isDirectory()) {
  25. // We support directories for looking up resources.
  26. // This is only useful for running libcore tests.
  27. elements.add(new Element(file, true, null, null));
  28. } else {
  29. System.logW("Unknown file type for: " + file);
  30. }
  31. }
  32. //……
  33. return elements.toArray(new Element[elements.size()]);
  34. }

DexPathList.java文件中:

  1. private static DexFile loadDexFile(File file, File optimizedDirectory)
  2. throws IOException {
  3. if (optimizedDirectory == null) {
  4. return new DexFile(file);
  5. } else {
  6. String optimizedPath = optimizedPathFor(file, optimizedDirectory);
  7. return DexFile.loadDex(file.getPath(), optimizedPath, 0);
  8. }
  9. }
  10.  
  11. //生成odex的目录
  12. private static String optimizedPathFor(File path,
  13. File optimizedDirectory) {
  14. String fileName = path.getName();
  15. if (!fileName.endsWith(DEX_SUFFIX)) {
  16. int lastDot = fileName.lastIndexOf(".");
  17. if (lastDot < 0) {
  18. fileName += DEX_SUFFIX;
  19. } else {
  20. StringBuilder sb = new StringBuilder(lastDot + 4);
  21. sb.append(fileName, 0, lastDot);
  22. sb.append(DEX_SUFFIX);
  23. fileName = sb.toString();
  24. }
  25. }
  26. File result = new File(optimizedDirectory, fileName);
  27. return result.getPath();
  28. }

optimizedPathFor主要是对文件的后缀进行修正,如果没有后缀名,就在末尾加上.dex,如果文件结尾不是.dex,就将后缀替换为.dex,然后创建我们的.dex文件,然后返回我们创建的.dex文件的路径,继续执行DexFile.loadDex() 函数:

  1. static public DexFile loadDex(String sourcePathName, String outputPathName,
  2. int flags) throws IOException {
  3. return new DexFile(sourcePathName, outputPathName, flags);
  4. }

这里直接返回了一个DexFile对象,下面来看看这个类的构造函数:

  1. //sourceName 就是我们要加载的自己的.jar文件路径
  2. // outputName 在optimizedPathFor() 函数中修正的加载.dex的路径
  3. private DexFile(String sourceName, String outputName, int flags) throws IOException {
  4. if (outputName != null) {
  5. try {
  6. String parent = new File(outputName).getParent();
  7. /* ??????*/
  8. if (Libcore.os.getuid() != Libcore.os.stat(parent).st_uid) {
  9. throw new IllegalArgumentException("Optimized data directory " + parent
  10. + " is not owned by the current user. Shared storage cannot protect"
  11. + " your application from code injection attacks.");
  12. }
  13. } catch (ErrnoException ignored) {
  14. // assume we'll fail with a more contextual error later
  15. }
  16. }
  17. //我们的重点就是在openDexFile()函数上
  18. mCookie = openDexFile(sourceName, outputName, flags);
  19. mFileName = sourceName;
  20. guard.open("close");
  21. //System.out.println("DEX FILE cookie is " + mCookie);
  22. }

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

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

在openDexFile()中只是调用了openDexFileNative () 继续跟入在\ dalvik\v m\nat ive\dalvik _sys tem_DexFile.cpp文件中的openDexFileNative() 函数,接下重点就在这个函数:

  1. static void Dalvik_dalvik_system_DexFile_openDexFileNative(const u4* args,
  2. JValue* pResult)
  3. {
  4. //args[0]: sourceName java层传入的
  5. //args[1]: outputName
  6. StringObject* sourceNameObj = (StringObject*) args[0];
  7. StringObject* outputNameObj = (StringObject*) args[1];
  8. DexOrJar* pDexOrJar = NULL;
  9. JarFile* pJarFile;
  10. RawDexFile* pRawDexFile;
  11. //DexOrJar* JarFile* RawDexFile* 目录
  12. char* sourceName;
  13. char* outputName;
  14. //……
  15. sourceName = dvmCreateCstrFromString(sourceNameObj);
  16. if (outputNameObj != NULL)
  17. outputName = dvmCreateCstrFromString(outputNameObj);
  18. else
  19. outputName = NULL;
  20. /*判断要加载的dex是否为系统中的dex文件
  21. * gDvm ???
  22. */
  23. if (dvmClassPathContains(gDvm.bootClassPath, sourceName)) {
  24. ALOGW("Refusing to reopen boot DEX '%s'", sourceName);
  25. dvmThrowIOException(
  26. "Re-opening BOOTCLASSPATH DEX files is not allowed");
  27. free(sourceName);
  28. free(outputName);
  29. RETURN_VOID();
  30. }
  31.  
  32. /*
  33. * Try to open it directly as a DEX if the name ends with ".dex".
  34. * If that fails (or isn't tried in the first place), try it as a
  35. * Zip with a "classes.dex" inside.
  36. */
  37. //判断sourcename扩展名是否是.dex
  38. if (hasDexExtension(sourceName)
  39. && dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) {
  40. ALOGV("Opening DEX file '%s' (DEX)", sourceName);
  41. pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
  42. pDexOrJar->isDex = true;
  43. pDexOrJar->pRawDexFile = pRawDexFile;
  44. pDexOrJar->pDexMemory = NULL;
  45. //.jar文件
  46. } else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) {
  47. ALOGV("Opening DEX file '%s' (Jar)", sourceName);
  48. pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
  49. pDexOrJar->isDex = false;
  50. pDexOrJar->pJarFile = pJarFile;
  51. pDexOrJar->pDexMemory = NULL;
  52. } else {
  53. //都不满足,抛出异常
  54. ALOGV("Unable to open DEX file '%s'", sourceName);
  55. dvmThrowIOException("unable to open DEX file");
  56. }
  57. if (pDexOrJar != NULL) {
  58. pDexOrJar->fileName = sourceName;
  59. //把pDexOr这个结构体中的内容加到gDvm中的userDexFile结构的hash表中,便于Dalvik以后的查找
  60. addToDexFileTable(pDexOrJar);
  61. } else {
  62. free(sourceName);
  63. }
  64. free(outputName);
  65. RETURN_PTR(pDexOrJar);
  66. }

接下来再看对.dex文件的处理函数dvmRawDexFileOpen 在dalvik\vm\RawDexFile.cpp文件中:

  1. /* See documentation comment in header. */
  2. int dvmRawDexFileOpen(const char* fileName, const char* odexOutputName,
  3. RawDexFile** ppRawDexFile, bool isBootstrap)
  4. {
  5. DvmDex* pDvmDex = NULL;
  6. char* cachedName = NULL;
  7. int result = -1;
  8. int dexFd = -1;
  9. int optFd = -1;
  10. u4 modTime = 0;
  11. u4 adler32 = 0;
  12. size_t fileSize = 0;
  13. bool newFile = false;
  14. bool locked = false;
  15. dexFd = open(fileName, O_RDONLY); //打开dex文件
  16. if (dexFd < 0) goto bail;
  17. /* If we fork/exec into dexopt, don't let it inherit the open fd. */
  18. dvmSetCloseOnExec(dexFd);//dexfd不继承
  19. //校验dex文件的标志,将第8字节开始的4个字节赋值给adler32。
  20. if (verifyMagicAndGetAdler32(dexFd, &adler32) < 0) {
  21. ALOGE("Error with header for %s", fileName);
  22. goto bail;
  23. }
  24. //得到dex文件的大小和修改时间,保存在modTime和filesize中
  25. if (getModTimeAndSize(dexFd, &modTime, &fileSize) < 0) {
  26. ALOGE("Error with stat for %s", fileName);
  27. goto bail;
  28. }
  29.  
  30. //odexOutputName就是odex文件名,如果odexOutputName为空,则自动生成一个。
  31. if (odexOutputName == NULL) {
  32. cachedName = dexOptGenerateCacheFileName(fileName, NULL);
  33. if (cachedName == NULL)
  34. goto bail;
  35. } else {
  36. cachedName = strdup(odexOutputName);
  37. }
  38. //主要是验证缓存文件名的正确性,之后将dexOptHeader结构写入fd中
  39. optFd = dvmOpenCachedDexFile(fileName, cachedName, modTime,
  40. adler32, isBootstrap, &newFile, /*createIfMissing=*/true);
  41. locked = true;
  42.  
  43. if (newFile) {
  44. u8 startWhen, copyWhen, endWhen;
  45. bool result;
  46. off_t dexOffset;
  47. dexOffset = lseek(optFd, 0, SEEK_CUR); //文件指针的位置
  48. result = (dexOffset > 0);
  49. if (result) {
  50. startWhen = dvmGetRelativeTimeUsec();
  51. //将dex文件中的内容拷贝到当前odex文件,也就是dexOffset开始
  52. result = copyFileToFile(optFd, dexFd, fileSize) == 0;
  53. copyWhen = dvmGetRelativeTimeUsec();
  54. }
  55. if (result) {
  56. //优化odex文件
  57. result = dvmOptimizeDexFile(optFd, dexOffset, fileSize,
  58. fileName, modTime, adler32, isBootstrap);
  59. }
  60. }
  61. /*
  62. * Map the cached version. This immediately rewinds the fd, so it
  63. * doesn't have to be seeked anywhere in particular.
  64. */
  65. //将odex文件映射到内存空间(mmap),并用mprotect将属性置为只读属性,并将映射的dex结构放在pDvmDex数据结构中,具体代码在下面。
  66. if (dvmDexFileOpenFromFd(optFd, &pDvmDex) != 0) {
  67. ALOGI("Unable to map cached %s", fileName);
  68. goto bail;
  69. }
  70. ……
  71. }
  1. //Dalvik/vm/RewDexFile.cpp
  2. static int verifyMagicAndGetAdler32(int fd, u4 *adler32)
  3. {
  4. u1 headerStart[];
  5. ssize_t amt = read(fd, headerStart, sizeof(headerStart));
  6. if (amt < ) {
  7. ALOGE("Unable to read header: %s", strerror(errno));
  8. return -;
  9. }
  10. if (amt != sizeof(headerStart)) {
  11. ALOGE("Unable to read full header (only got %d bytes)", (int) amt);
  12. return -;
  13. }
  14. if (!dexHasValidMagic((DexHeader*) (void*) headerStart)) {
  15. return -;
  16. }
  17. *adler32 = (u4) headerStart[]
  18. | (((u4) headerStart[]) << )
  19. | (((u4) headerStart[]) << )
  20. | (((u4) headerStart[]) << );
  21.  
  22. return ;
  23. }
  1. //dalvik\vm\DvmDex.cpp
  2. /*
  3. * Given an open optimized DEX file, map it into read-only shared memory and
  4. * parse the contents.
  5. *
  6. * Returns nonzero on error.
  7. */
  8. int dvmDexFileOpenFromFd(int fd, DvmDex** ppDvmDex)
  9. {
  10. DvmDex* pDvmDex;
  11. DexFile* pDexFile;
  12. MemMapping memMap;
  13. int parseFlags = kDexParseDefault;
  14. int result = -;
  15.  
  16. if (gDvm.verifyDexChecksum)
  17. parseFlags |= kDexParseVerifyChecksum;
  18. if (lseek(fd, , SEEK_SET) < ) {
  19. ALOGE("lseek rewind failed");
  20. goto bail;
  21. }
  22. //mmap映射fd文件,就是我们之前的odex文件
  23. if (sysMapFileInShmemWritableReadOnly(fd, &memMap) != ) {
  24. ALOGE("Unable to map file");
  25. goto bail;
  26. }
  27. pDexFile = dexFileParse((u1*)memMap.addr, memMap.length, parseFlags);
  28. if (pDexFile == NULL) {
  29. ALOGE("DEX parse failed");
  30. sysReleaseShmem(&memMap);
  31. goto bail;
  32. }
  33. pDvmDex = allocateAuxStructures(pDexFile);
  34. if (pDvmDex == NULL) {
  35. dexFileFree(pDexFile);
  36. sysReleaseShmem(&memMap);
  37. goto bail;
  38. }
  39. /* tuck this into the DexFile so it gets released later */
  40. //将映射odex文件的内存拷贝到DvmDex的结构中
  41. sysCopyMap(&pDvmDex->memMap, &memMap);
  42. pDvmDex->isMappedReadOnly = true;
  43. *ppDvmDex = pDvmDex;
  44. result = ;
  45.  
  46. bail:
  47. return result;
  48. }
  49.  
  50. /*dalvik\libdex\SysUtil.cpp
  51. */
  52. int sysMapFileInShmemWritableReadOnly(int fd, MemMapping* pMap)
  53. {
  54. off_t start;
  55. size_t length;
  56. void* memPtr;
  57. assert(pMap != NULL);
  58. //获得文件长度和文件开始地址
  59. if (getFileStartAndLength(fd, &start, &length) < )
  60. return -;
  61. //映射文件
  62. memPtr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE,
  63. fd, start);
  64. //……
  65. //将保护属性置为只读属性
  66. if (mprotect(memPtr, length, PROT_READ) < ) {
  67. //…….
  68. }
  69. pMap->baseAddr = pMap->addr = memPtr;
  70. pMap->baseLength = pMap->length = length;
  71. return ;
  72. //……
  73. }

下面在分析文件后缀不是.dex的情况:

  1. /*如果不是.dex文件*/
  2. int dvmJarFileOpen(const char* fileName, const char* odexOutputName,
  3. JarFile** ppJarFile, bool isBootstrap)
  4. {
  5. ZipArchive archive;
  6. DvmDex* pDvmDex = NULL;
  7. char* cachedName = NULL;
  8. bool archiveOpen = false;
  9. bool locked = false;
  10. int fd = -;
  11. int result = -;
  12.  
  13. //打开.jar文件并映射,内存结构放在ZipArchive中,之后将具体分析的代码
  14. if (dexZipOpenArchive(fileName, &archive) != )
  15. goto bail;
  16. archiveOpen = true;
  17. dvmSetCloseOnExec(dexZipGetArchiveFd(&archive)); //不继承
  18.  
  19. // openAlternateSuffix函数将fileName的后缀名改为”.odex”,例如
  20. //”Hello.jar”--”Hello.odex”,然后调用open()”打开”Hello.odex文件
  21. //如果成功返回”Hello.odex”的文件描述符
  22. fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName);
  23. if (fd >= ) {
  24. ALOGV("Using alternate file (odex) for %s ...", fileName);
  25. //…检验optHeader
  26. if (!dvmCheckOptHeaderAndDependencies(fd, false, , , true, true)) {
  27. //……
  28. goto tryArchive;
  29. }
  30. } else {
  31. ZipEntry entry;
  32. tryArchive:
  33. /*
  34. * Pre-created .odex absent or stale. Look inside the jar for a
  35. * "classes.dex".
  36. */
  37. // static const char* kDexInJarName = "classes.dex";
  38. /*
  39. 在dexZipFindEntry函数中,对kDexInJarName也就是”class.dex”进行hash运算,找到”class.dex”在archive结构中的表项
  40. */
  41. entry = dexZipFindEntry(&archive, kDexInJarName);
  42. if (entry != NULL) {
  43. bool newFile = false;
  44.  
  45. //如果odex缓存路径为空,则自动生成一个路径
  46. if (odexOutputName == NULL) {
  47. cachedName = dexOptGenerateCacheFileName(fileName,
  48. kDexInJarName);
  49. if (cachedName == NULL)
  50. goto bail;
  51. } else {
  52. cachedName = strdup(odexOutputName);
  53. }
  54. //创建cachedName对应的文件 (.odex)
  55. fd = dvmOpenCachedDexFile(fileName, cachedName,
  56. dexGetZipEntryModTime(&archive, entry),
  57. dexGetZipEntryCrc32(&archive, entry),
  58. //……
  59. locked = true;
  60. //……
  61. if (newFile) { //成功创建.odex文件
  62. u8 startWhen, extractWhen, endWhen;
  63. bool result;
  64. off_t dexOffset;
  65. dexOffset = lseek(fd, , SEEK_CUR);
  66. result = (dexOffset > );
  67. if (result) {
  68. startWhen = dvmGetRelativeTimeUsec();
  69. result = dexZipExtractEntryToFile(&archive, entry, fd) == ;
  70. extractWhen = dvmGetRelativeTimeUsec();
  71. }
  72. if (result) {
  73. //优化dex文件-.odex
  74. result = dvmOptimizeDexFile(fd, dexOffset,
  75. dexGetZipEntryUncompLen(&archive, entry),
  76. fileName,
  77. dexGetZipEntryModTime(&archive, entry),
  78. dexGetZipEntryCrc32(&archive, entry),
  79. isBootstrap);
  80. }
  81. //已经得到了.odex文件,下面的流程就和.dex文件一样了。
  82. //映射.odex文件,
  83. if (dvmDexFileOpenFromFd(fd, &pDvmDex) != )
  84. //…………
  85. return result;
  86. }
  1. //\dalvik\libdex\SysUtil.cpp
  2. int dexZipOpenArchive(const char* fileName, ZipArchive* pArchive)
  3. {
  4. int fd, err;
  5. …….
  6. memset(pArchive, , sizeof(ZipArchive));
  7. //打开文件
  8. fd = open(fileName, O_RDONLY | O_BINARY, );
  9. ……
  10. return dexZipPrepArchive(fd, fileName, pArchive);
  11. }
  12.  
  13. int dexZipPrepArchive(int fd, const char* debugFileName, ZipArchive* pArchive)
  14. {
  15. int result = -;
  16. memset(pArchive, , sizeof(*pArchive));
  17. pArchive->mFd = fd; //Zip的文件描述符
  18. if (mapCentralDirectory(fd, debugFileName, pArchive) != )
  19. goto bail;
  20. if (parseZipArchive(pArchive) != ) {
  21. goto bail;
  22. }
  23. /* success */
  24. result = ;
  25. bail:
  26. if (result != )
  27. dexZipCloseArchive(pArchive); //失败释放pArchive结构
  28. return result;
  29. }
  30.  
  31. static int mapCentralDirectory(int fd, const char* debugFileName,
  32. ZipArchive* pArchive)
  33. {
  34. /*
  35. * Get and test file length.
  36. */
  37. //检验文件长度的有效性
  38. off64_t fileLength = lseek64(fd, , SEEK_END);
  39. if (fileLength < kEOCDLen) {
  40. return -;
  41. }
  42. size_t readAmount = kMaxEOCDSearch;
  43. if (fileLength < off_t(readAmount))
  44. readAmount = fileLength;
  45.  
  46. u1* scanBuf = (u1*) malloc(readAmount);
  47. if (scanBuf == NULL) {
  48. return -;
  49. }
  50. int result = mapCentralDirectory0(fd, debugFileName, pArchive,
  51. fileLength, readAmount, scanBuf);
  52. free(scanBuf);
  53. return result;
  54. }
  55.  
  56. tatic int mapCentralDirectory0(int fd, const char* debugFileName,
  57. ZipArchive* pArchive, off64_t fileLength, size_t readAmount, u1* scanBuf)
  58. {
  59. /*
  60. * Make sure this is a Zip archive.
  61. */
  62. //校验文件是否合法的Zip文件
  63. //…… //偏移16的地方 //偏移12
  64. if (sysMapFileSegmentInShmem(fd, centralDirOffset, centralDirSize,
  65. &pArchive->mDirectoryMap) != )
  66. {
  67. ALOGW("Zip: cd map failed");
  68. return -;
  69. }
  70. pArchive->mNumEntries = numEntries;
  71. pArchive->mDirectoryOffset = centralDirOffset;
  72. return ;
  73. }
  74.  
  75. int sysMapFileSegmentInShmem(int fd, off_t start, size_t length,
  76. MemMapping* pMap)
  77. {
  78. size_t actualLength;
  79. off_t actualStart;
  80. int adjust;
  81. void* memPtr;
  82. assert(pMap != NULL);
  83. /* adjust to be page-aligned */
  84. adjust = start % SYSTEM_PAGE_SIZE;
  85. actualStart = start - adjust;
  86. actualLength = length + adjust;
  87. //映射
  88. memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED,
  89. fd, actualStart);
  90. // …….
  91. pMap->baseAddr = memPtr;
  92. pMap->baseLength = actualLength;
  93. pMap->addr = (char*)memPtr + adjust;
  94. pMap->length = length;
  95. return ;
  96. }

ZipArchive的结构体如下:

  1. struct ZipArchive {
  2. /* open Zip archive */
  3. int mFd; //打开的zip文件
  4. /* mapped central directory area */
  5. off_t mDirectoryOffset;
  6. MemMapping mDirectoryMap; //映射内存的结构
  7. /* number of entries in the Zip archive */
  8. int mNumEntries; //
  9. int mHashTableSize; //名字hash表的大小
  10. ZipHashEntry* mHashTable; //hash表的表项,
  11. };
  12.  
  13. struct ZipHashEntry {
  14. const char* name;
  15. unsigned short nameLen;
  16. };

我们可以简要总结下整个的加载流程,首先是对文件名的修正,后缀名置为”.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中进行导入,阅读。其次不得不说阅读源码真的是一个比较痛苦的过程,但真的学习下来,收获还是很大的。

浅析dex文件加载机制的更多相关文章

  1. Android 的 so 文件加载机制

    本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 最近碰到一些 so 文件问题,顺便将相关知识点梳理一下. 提问 本文的结论是跟着 System.loadlibrary() 一层层源 ...

  2. 插件化框架解读之so 文件加载机制(四)

    阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680 提问 本文的结论是跟着 System.loadlibrary() ...

  3. Android中的动态加载机制

    在目前的软硬件环境下,Native App与Web App在用户体验上有着明显的优势,但在实际项目中有些会因为业务的频繁变更而频繁的升级客户端,造成较差的用户体验,而这也恰恰是Web App的优势.本 ...

  4. Android 动态加载 (一) 态加载机制 案例一

    在目前的软硬件环境下,Native App与Web App在用户体验上有着明显的优势,但在实际项目中有些会因为业务的频繁变更而频繁的升级客户端,造成较差的用户体验,而这也恰恰是Web App的优势.本 ...

  5. Java_类文件及加载机制

    类文件及类加载机制 标签(空格分隔): Java 本篇博客的重点是分析JVM是如何进行类的加载的,但同时我们会捎带着说一下Class类文件结构,以便对类加载机制有更深的理解. 类文件结构 平台无关性 ...

  6. Dex动态加载

    Dex动态加载是为了解决什么问题? 在Android系统中,一个App的所有代码都在一个Dex文件里面. Dex是一个类似Jar的存储了多个Java编译字节码的归档文件. 因为Android系统使用D ...

  7. jvm系列(一):java类的加载机制

    java类的加载机制 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装 ...

  8. Yii2的深入学习--自动加载机制

    Yii2 的自动加载分两部分,一部分是 Composer 的自动加载机制,另一部分是 Yii2 框架自身的自动加载机制. Composer自动加载 对于库的自动加载信息,Composer 生成了一个  ...

  9. Yii2的深入学习--自动加载机制(转)

    Yii2 的自动加载分两部分,一部分是 Composer 的自动加载机制,另一部分是 Yii2 框架自身的自动加载机制. Composer自动加载 对于库的自动加载信息,Composer 生成了一个  ...

随机推荐

  1. [转]ubuntu 12.04添加launcher方法

    [转]ubuntu 12.04添加launcher方法 http://www.cnblogs.com/Jerryshome/archive/2012/08/21/2649500.html 对ubunt ...

  2. perl DBI 学习总结(转载)

    perl DBI 学习总结 源文地址:http://blog.csdn.net/like_zhz/article/details/5441946 DBI和DBD的不同关系模型: ########### ...

  3. Qt 窗口等设置

    摘要: -------------------------------------------------- qt固定窗口大小: 一种方法是设置它的最大大小和最小大小,并且使它们的值相等,都等于当前你 ...

  4. ubuntu 修改主机名

    sudo gedit /etc/hostname sudo gedit /etc/hosts

  5. < java.util >-- Collection接口

    Collection:    |--List:有序(元素存入集合的顺序和取出的顺序一致),元素都有索引.元素可以重复.    |--Set:无序(存入和取出顺序有可能不一致),不可以存储重复元素.必须 ...

  6. 微软职位内部推荐-Senior Software Engineer-News

    微软近期Open的职位: News is a critical areas for integration of mobile and services, one of the top priorit ...

  7. Valuable site on github

    https://thegrid.io/?utm_source=adwords&utm_medium=cpc&utm_campaign=thegrid-display-english&a ...

  8. ERP系统实施与企业内部控制管理实践

    COSO内部控制体系包含5 个要素,分别为控制环境.风险评估.控制活动.信息与沟通.监督,涉及公司层面的控制.业务活动的控制以及信息系统总体控制.随着ERP系统的上线运行,企业的内部控制体系建设应与E ...

  9. OGNL表达式介绍

    OGNL是Object-Graph Navigation Language的缩写,它是一种功能强大的表达式语言(Expression Language,简称为EL),通过它简单一致的表达式语法,可以存 ...

  10. 深入理解CSS3 animation的steps

    在应用 CSS3 渐变/动画时,有个控制时间的属性 <timing-function> .它的取值中除了常用到的三次贝塞尔曲线以外,还有个让人比较困惑的 steps() 函数. steps ...