简书链接:官方multidex的加载过程分析然后研究研究能否抄袭一点点技术
文章字数:557,阅读全文大约需要2分钟
1、检测jvm版本,正则 2.1.0 那么就是支持jvm,那么默认是不是进行dex的合并。
. doInstallation方法获取到了ApplicationInfo
1 2 3 4 5
   | mainContext: Context  = {AppContext@4800}  sourceApk: File  = {File@4821} "/data/app/cn.qssq666.robot-2/base.apk" dataDir: File  = {File@4822} "/data/user/0/cn.qssq666.robot" secondaryFolderName: String  = {@4823} "secondary-dexes" prefsKeyPrefix: String  = {@4824} ""
  | 
 
2、clearOldDexDir第二步检查包名/files/secondary-dexes/是否存在存在则删除。里面所有文件,还把文件夹也删除了.
. /data/user/0/cn.qssq666.robot/code_cache/secondary-dexes
检查是否更改,更改就走另外一个方法否则走performExtractions
大概逻辑是读取apk的classdex 2-最终的dex全部打包(extract方法打包)成zip文件
如先把classes2.dex解压打包成临时文件
/data/user/0/cn.qssq666.robot/code_cache/secondary-dexes/tmp-base.apk.classes238553433.zip
然后重命名为为/data/user/0/cn.qssq666.robot/code_cache/secondary-dexes/base.apk.classes2.zip
打包的代码分析
1 2 3 4 5 6 7 8 9 10 11 12 13
   | InputStream in = apk.getInputStream(dexFile);         ZipOutputStream out = null;         File tmp = File.createTempFile("tmp-" + extractedFilePrefix, ".zip", extractTo.getParentFile());         Log.i("MultiDex", "Extracting " + tmp.getPath());
          try {             out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(tmp)));
              try {                 ZipEntry classesDex = new ZipEntry("classes.dex");                 classesDex.setTime(dexFile.getTime());                 out.putNextEntry(classesDex);                 byte[] buffer 
   | 
 
从zipentry实体获取entry的输入流然后创建一个ZipOutputStream输出流,然后给他弄一个时间,然后zip的实体名字叫classes.dex也就是每一个zip文件都有一个classes.dex很显然,他的合并技术和我用的方法完全不同。是把一个包含多个dex的apk变成每一个apk,(当然这里叫zip)然后里面有一个classes.dex

最终的成果

然后
/data/user/0/cn.qssq666.robot/shared_prefs/multidex.version.xml
把时间戳和zip的crc校验码存起来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
   |  private static void putStoredApkInfo(Context context, String keyPrefix, long timeStamp, long crc, List<MultiDexExtractor.ExtractedDex> extractedDexes) {     SharedPreferences prefs = getMultiDexPreferences(context);     Editor edit = prefs.edit();     edit.putLong(keyPrefix + "timestamp", timeStamp);     edit.putLong(keyPrefix + "crc", crc);     edit.putInt(keyPrefix + "dex.number", extractedDexes.size() + 1);     int extractedDexId = 2;
      for(Iterator var10 = extractedDexes.iterator(); var10.hasNext(); ++extractedDexId) {         MultiDexExtractor.ExtractedDex dex = (MultiDexExtractor.ExtractedDex)var10.next();         edit.putLong(keyPrefix + "dex.crc." + extractedDexId, dex.crc);         edit.putLong(keyPrefix + "dex.time." + extractedDexId, dex.lastModified());     }
      edit.commit(); }
 
 
  | 
 
这才完成了
1 2
   | List<? extends File> files = MultiDexExtractor.load(mainContext, sourceApk, dexDir, prefsKeyPrefix, false);                
   | 
 
的逻辑
然后走
 installSecondaryDexes(loader, dexDir, files);
具体逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
   |     }
      private static final class V19 {         private V19() {         }
          private static void install(ClassLoader loader, List<? extends File> additionalClassPathEntries, File optimizedDirectory) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException {             Field pathListField = MultiDex.findField(loader, "pathList");             Object dexPathList = pathListField.get(loader);             ArrayList<IOException> suppressedExceptions = new ArrayList();             MultiDex.expandFieldArray(dexPathList, "dexElements", makeDexElements(dexPathList, new ArrayList(additionalClassPathEntries), optimizedDirectory, suppressedExceptions));             if (suppressedExceptions.size() > 0) {                 Iterator var6 = suppressedExceptions.iterator();
                  while(var6.hasNext()) {                     IOException e = (IOException)var6.next();                     Log.w("MultiDex", "Exception in makeDexElement", e);                 }
                  Field suppressedExceptionsField = MultiDex.findField(dexPathList, "dexElementsSuppressedExceptions");                 IOException[] dexElementsSuppressedExceptions = (IOException[])((IOException[])suppressedExceptionsField.get(dexPathList));                 if (dexElementsSuppressedExceptions == null) {                     dexElementsSuppressedExceptions = (IOException[])suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);                 } else {                     IOException[] combined = new IOException[suppressedExceptions.size() + dexElementsSuppressedExceptions.length];                     suppressedExceptions.toArray(combined);                     System.arraycopy(dexElementsSuppressedExceptions, 0, combined, suppressedExceptions.size(), dexElementsSuppressedExceptions.length);                     dexElementsSuppressedExceptions = combined;                 }
                  suppressedExceptionsField.set(dexPathList, dexElementsSuppressedExceptions);             }
          }
          private static Object[] makeDexElements(Object dexPathList, ArrayList<File> files, File optimizedDirectory, ArrayList<IOException> suppressedExceptions) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {             Method makeDexElements = MultiDex.findMethod(dexPathList, "makeDexElements", ArrayList.class, File.class, ArrayList.class);             return (Object[])((Object[])makeDexElements.invoke(dexPathList, files, optimizedDirectory, suppressedExceptions));         }     } }
   | 
 
很遗憾,这个v19的代码在我的23的手机并不能执行,
因为PathClassloader没有makeDexElements方法。
这里就不继续探索了,看看源码有哪几个把,copy过来改一下看看

首先要让IS_VM_MULTIDEX_CAPABLE变成非常量,这样才好玩嘛。