Multidex详解
来源:互联网 发布:b超三个数据看男女技巧 编辑:程序博客网 时间:2024/06/01 10:14
Multidex详解
1.使用
1.在项目gralde配置中增加依赖 compile "com.android.support:multidex:1.0.2"2.在AndroidManifest.xml中声明 application为MultiDexApplication, 或者在自定义的application中,重写如下方法 protected void attachBaseContext(Context base) { super.attachBaseContext(base); android.support.multidex.MultiDex.install(this); }
2.源码详解
public static void install(Context context) { Log.i("MultiDex", "Installing application"); if(IS_VM_MULTIDEX_CAPABLE) {// vm支持的不需要使用multidex库 Log.i("MultiDex", "VM has multidex support, MultiDex support library is disabled."); } else if(VERSION.SDK_INT < 4) {// 太早Android版本不支持的 throw new RuntimeException("MultiDex installation failed. SDK " + VERSION.SDK_INT + " is unsupported. Min SDK version is " + 4 + "."); } else { try { //获取安装的apk的信息 ApplicationInfo applicationInfo = getApplicationInfo(context); if(applicationInfo == null) { Log.i("MultiDex", "No ApplicationInfo available, i.e. running on a test Context: MultiDex support library is disabled."); return; } // 开始执行加载其他dex, 加载到data/data/包名/code_cache/secondary-dexes/ doInstallation(context, new File(applicationInfo.sourceDir), new File(applicationInfo.dataDir), "secondary-dexes", ""); } catch (Exception var2) { Log.e("MultiDex", "MultiDex installation failure", var2); throw new RuntimeException("MultiDex installation failed (" + var2.getMessage() + ")."); } Log.i("MultiDex", "install done"); } } private static void doInstallation(Context mainContext, File sourceApk, File dataDir, String secondaryFolderName, String prefsKeyPrefix) throws IOException, IllegalArgumentException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException { Set var5 = installedApk;// 已经加载不再重新加载 synchronized(installedApk) { if(!installedApk.contains(sourceApk)) { installedApk.add(sourceApk); .... ClassLoader loader; try { loader = mainContext.getClassLoader(); } catch (RuntimeException var11) { Log.w("MultiDex", "Failure while trying to obtain Context class loader. Must be running in test mode. Skip patching.", var11); return; } if(loader == null) { Log.e("MultiDex", "Context class loader is null. Must be running in test mode. Skip patching."); } else { try { clearOldDexDir(mainContext); } catch (Throwable var10) { Log.w("MultiDex", "Something went wrong when trying to clear old MultiDex extraction, continuing without cleaning.", var10); } // 获取存放目录 File dexDir = getDexDir(mainContext, dataDir, secondaryFolderName); // 加载dex List<? extends File> files = MultiDexExtractor.load(mainContext, sourceApk, dexDir, prefsKeyPrefix, false); // 合并使用dex installSecondaryDexes(loader, dexDir, files); } } } }
3.加载过程
static List<? extends File> load(Context context, File sourceApk, File dexDir, String prefsKeyPrefix, boolean forceReload) throws IOException { ... // 是否强制重新加载或dex文件被修改了, 以apk的最后修改时间和zip的crc来校验apk是否被修改 if(!forceReload && !isModified(context, sourceApk, currentCrc, prefsKeyPrefix)) { try { files = loadExistingExtractions(context, sourceApk, dexDir, prefsKeyPrefix); } catch (IOException var21) { Log.w("MultiDex", "Failed to reload existing extracted secondary dex files, falling back to fresh extraction", var21); files = performExtractions(sourceApk, dexDir); putStoredApkInfo(context, prefsKeyPrefix, getTimeStamp(sourceApk), currentCrc, files); } } else { Log.i("MultiDex", "Detected that extraction must be performed."); // 准备dex files = performExtractions(sourceApk, dexDir); // 保存dex的信息(最后时间和crc信息)到multidex.version的sp配置中 putStoredApkInfo(context, prefsKeyPrefix, getTimeStamp(sourceApk), currentCrc, files); } ... } private static List<ExtractedDex> performExtractions(File sourceApk, File dexDir) throws IOException { .... int secondaryNumber = 2; // 从apk中找出dex文件 for(ZipEntry dexFile = apk.getEntry("classes" + secondaryNumber + ".dex"); dexFile != null; dexFile = apk.getEntry("classes" +secondaryNumber + ".dex")) { String fileName = extractedFilePrefix + secondaryNumber + ".zip";// 创建dex对应的zip文件 ExtractedDex extractedFile = new ExtractedDex(dexDir, fileName); files.add(extractedFile); Log.i("MultiDex", "Extraction is needed for file " + extractedFile); int numAttempts = 0; boolean isExtractionSuccessful = false; while(numAttempts < 3 && !isExtractionSuccessful) { ++numAttempts; // 把dex文件提取出来放到对应的zip文件中, 重试3次 extract(apk, dexFile, extractedFile, extractedFilePrefix); try { extractedFile.crc = getZipCrc(extractedFile); isExtractionSuccessful = true; } catch (IOException var19) { isExtractionSuccessful = false; Log.w("MultiDex", "Failed to read crc from " + extractedFile.getAbsolutePath(), var19); } Log.i("MultiDex", "Extraction " + (isExtractionSuccessful?"succeeded":"failed") + " - length " + extractedFile.getAbsolutePath() + ": " + extractedFile.length() + " - crc: " + extractedFile.crc); if(!isExtractionSuccessful) { extractedFile.delete(); if(extractedFile.exists()) { Log.w("MultiDex", "Failed to delete corrupted secondary dex '" + extractedFile.getPath() + "'"); } } } if(!isExtractionSuccessful) { throw new IOException("Could not create zip file " + extractedFile.getAbsolutePath() + " for secondary dex (" + secondaryNumber + ")"); } ++secondaryNumber; } ... } private static void putStoredApkInfo(Context context, String keyPrefix, long timeStamp, long crc, List<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) { ExtractedDex dex = (ExtractedDex)var10.next(); edit.putLong(keyPrefix + "dex.crc." + extractedDexId, dex.crc); edit.putLong(keyPrefix + "dex.time." + extractedDexId, dex.lastModified()); } edit.commit(); }
4.合并dex
private static void installSecondaryDexes(ClassLoader loader, File dexDir, List<? extends File> files) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException, IOException { if(!files.isEmpty()) { if(VERSION.SDK_INT >= 19) { V19.install(loader, files, dexDir); } else if(VERSION.SDK_INT >= 14) { V14.install(loader, files, dexDir); } else { V4.install(loader, files); } } } 这里先说明下原理: //dalvik.system.DexPathList android遍历dexElements的来加载类的, 所以只要把dex的dexElements合并到apk的pathClassLoade的dexElements中就可以加载到dex中的类 public Class findClass(String name, List<Throwable> suppressed) { for (Element element : dexElements) { DexFile dex = element.dexFile; if (dex != null) { Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed); if (clazz != null) { return clazz; } } } if (dexElementsSuppressedExceptions != null) { suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions)); } return null; } 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(); 合并dex的dexElements到pathClassLoade的dexElements中, 通过反射实现 MultiDex.expandFieldArray(dexPathList, "dexElements", makeDexElements(dexPathList, new ArrayList(additionalClassPathEntries), optimizedDirectory, suppressedExceptions)); ... } // 构建dex的DexElements, 在Android6.0之后的DexPathList没有makeDexElements这个方法, 这就是前面判断vm自身支持multidex,不需要再使用multidex库了 private static Object[] makeDexElements(Object dexPathList, ArrayList<File> files, File optimizedDirectory, ArrayList<IOException> suppressedExceptions) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { Method makeDexElements = MultiDex.findMethod(dexPathList, "makeDexElements", new Class[]{ArrayList.class, File.class, ArrayList.class}); return (Object[])((Object[])makeDexElements.invoke(dexPathList, new Object[]{files, optimizedDirectory, suppressedExceptions})); } }
阅读全文
0 0
- Multidex详解
- Android分包MultiDex原理详解
- Android分包MultiDex原理详解
- Android分包MultiDex原理详解
- Android分包MultiDex原理详解
- Android分包MultiDex原理详解
- Android分包MultiDex原理详解
- Android分包MultiDex原理详解
- Android分包MultiDex原理详解
- MultiDex
- MultiDex
- MultiDex
- multidex
- multidex
- Android关于Dex拆分(MultiDex)技术详解
- Android关于Dex拆分(MultiDex)技术详解
- Android关于Dex拆分(MultiDex)技术详解
- MultiDex源码
- TCP/IP、Http、Socket的区别
- STL中的空间配置器allocator的实现原理及源码剖析
- java线程池
- iOS nomatic strong,weak,retain,assign,copy 等的区别。
- 一个链表中包含环,请找出该链表的环的入口结点
- Multidex详解
- git教程
- nodejs 学习记录(三)-mime模块学习
- 初学c#知识整理(二)
- Google VR VIEW FOR THE WEB
- 软件/插件推荐
- Python自学笔记
- 介绍几种范强的方式 长期更新
- 如何读懂火焰图?