加载多个dex
来源:互联网 发布:同花顺中天软件下载 编辑:程序博客网 时间:2024/06/05 01:05
下面是多dex加载的时序图:
Android项目有两种方式支持多dex:
1. 项目中的Application类继承MultiDexApplication。
2. 在自己的Application类的attachBaseContext方法中调用MultiDex.install(this);。
我从MultiDexApplication这个类开始分析。
MultiDexApplication类继承了Application,并重载了attachBaseContext方法,在这个方法中调用了MultiDex.install(this);。
[java] view plain copy
- public class MultiDexApplication extends Application {
- @Override
- protected void attachBaseContext(Context base) {
- super.attachBaseContext(base);
- MultiDex.install(this);
- }
- }
MultiDex.install方法:
[java] view plain copy
- /**
- * Patches the application context class loader by appending extra dex files
- * loaded from the application apk. This method should be called in the
- * attachBaseContext of your {@link Application}, see
- * {@link MultiDexApplication} for more explanation and an example.
- *
- * @param context application context.
- * @throws RuntimeException if an error occurred preventing the classloader
- * extension.
- */
- public static void install(Context context) {
- Log.i(TAG, "install");
- ......
- try {
- ApplicationInfo applicationInfo = getApplicationInfo(context);
- if (applicationInfo == null) {
- // Looks like running on a test Context, so just return without patching.
- return;
- }
- synchronized (installedApk) {
- String apkPath = applicationInfo.sourceDir;
- // installedApk的类型是:Set<String>。
- // 如果这个apk已经安装,则不重复安装。
- if (installedApk.contains(apkPath)) {
- return;
- }
- installedApk.add(apkPath);
- ......
- // 类加载器应该直接或间接继承于BaseDexClassLoader。
- // 修改BaseDexClassLoader类中的DexPathList pathList字段,追加额外的DEX文件项。
- /* The patched class loader is expected to be a descendant of
- * dalvik.system.BaseDexClassLoader. We modify its
- * dalvik.system.DexPathList pathList field to append additional DEX
- * file entries.
- */
- ClassLoader loader;
- ......
- // dex将会输出到SECONDARY_FOLDER_NAME目录。
- File dexDir = new File(applicationInfo.dataDir, SECONDARY_FOLDER_NAME);
- List<File> files = MultiDexExtractor.load(context, applicationInfo, dexDir, false);
- // 校验这些zip文件是否合法。
- if (checkValidZipFiles(files)) {
- // 安装提取出来的zip文件。
- installSecondaryDexes(loader, dexDir, files);
- } else {
- Log.w(TAG, "Files were not valid zip files. Forcing a reload.");
- // 最后一个参数是true,代表强制加载。
- // Try again, but this time force a reload of the zip file.
- files = MultiDexExtractor.load(context, applicationInfo, dexDir, true);
- // 校验这些zip文件是否合法。
- if (checkValidZipFiles(files)) {
- // 安装提取出来的zip文件。
- installSecondaryDexes(loader, dexDir, files);
- } else {
- // Second time didn't work, give up
- throw new RuntimeException("Zip files were not valid.");
- }
- }
- }
- } catch (Exception e) {
- Log.e(TAG, "Multidex installation failure", e);
- throw new RuntimeException("Multi dex installation failed (" + e.getMessage() + ").");
- }
- Log.i(TAG, "install done");
- }
MultiDexExtractor.load获得/导出apk中多出的dex,这些dex导出后会被打包成zip文件:
[java] view plain copy
- /**
- * 提取/获得apk中多dex的提取zip文件。
- * 如果不是加载已经存在的文件的情况,则还要保存apk的信息:时间戳、crc值、apk中dex的总个数。
- *
- * Extracts application secondary dexes into files in the application data
- * directory.
- *
- * @return a list of files that were created. The list may be empty if there
- * are no secondary dex files.
- * @throws IOException if encounters a problem while reading or writing
- * secondary dex files
- */
- static List<File> load(Context context, ApplicationInfo applicationInfo, File dexDir,
- boolean forceReload) throws IOException {
- Log.i(TAG, "MultiDexExtractor.load(" + applicationInfo.sourceDir + ", " + forceReload + ")");
- final File sourceApk = new File(applicationInfo.sourceDir);
- long currentCrc = getZipCrc(sourceApk);
- List<File> files;
- // isModified方法判断apk是否被修改过。
- if (!forceReload && !isModified(context, sourceApk, currentCrc)) {
- try {
- // 加载已经存在的文件,如果有的文件不存在,或者不是zip文件,则会抛出异常。
- files = loadExistingExtractions(context, sourceApk, dexDir);
- } catch (IOException ioe) {
- Log.w(TAG, "Failed to reload existing extracted secondary dex files,"
- + " falling back to fresh extraction", ioe);
- // 从apk中提取出多dex,然后将这些dex逐个打包为zip文件,最终返回提取出来的zip文件列表。
- files = performExtractions(sourceApk, dexDir);
- // getTimeStamp方法中调用的是sourceApk.lastModified()方法。
- // putStoredApkInfo方法存储apk的信息:时间戳、crc值、apk中dex的总个数。
- putStoredApkInfo(context, getTimeStamp(sourceApk), currentCrc, files.size() + 1);
- }
- } else {
- Log.i(TAG, "Detected that extraction must be performed.");
- // 这里的performExtractions和putStoredApkInfo同上。
- files = performExtractions(sourceApk, dexDir);
- putStoredApkInfo(context, getTimeStamp(sourceApk), currentCrc, files.size() + 1);
- }
- Log.i(TAG, "load found " + files.size() + " secondary dex files");
- return files;
- }
MultiDexExtractor.performExtractions方法:
[java] view plain copy
- /**
- * 从apk中提取出多dex,然后将这些dex逐个打包为zip文件。
- * @param sourceApk apk文件。
- * @param dexDir 输出目录。
- * @return 提取出来的zip文件列表。
- */
- private static List<File> performExtractions(File sourceApk, File dexDir)
- throws IOException {
- final String extractedFilePrefix = sourceApk.getName() + EXTRACTED_NAME_EXT;
- // 如果文件没有正确的前缀,则删除。
- // Ensure that whatever deletions happen in prepareDexDir only happen if the zip that
- // contains a secondary dex file in there is not consistent with the latest apk. Otherwise,
- // multi-process race conditions can cause a crash loop where one process deletes the zip
- // while another had created it.
- prepareDexDir(dexDir, extractedFilePrefix);
- List<File> files = new ArrayList<File>();
- final ZipFile apk = new ZipFile(sourceApk);
- try {
- int secondaryNumber = 2;
- ZipEntry dexFile = apk.getEntry(DEX_PREFIX + secondaryNumber + DEX_SUFFIX);
- while (dexFile != null) {
- // 输出的文件名。
- String fileName = extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX;
- // 输出的文件。
- File extractedFile = new File(dexDir, fileName);
- files.add(extractedFile);
- Log.i(TAG, "Extraction is needed for file " + extractedFile);
- int numAttempts = 0;
- boolean isExtractionSuccessful = false;
- while (numAttempts < MAX_EXTRACT_ATTEMPTS && !isExtractionSuccessful) {
- numAttempts++;
- // 提取apk中的多dex文件,然后打包成一个zip文件。
- // Create a zip file (extractedFile) containing only the secondary dex file
- // (dexFile) from the apk.
- extract(apk, dexFile, extractedFile, extractedFilePrefix);
- // 验证提取的文件是否是一个zip文件。
- // Verify that the extracted file is indeed a zip file.
- isExtractionSuccessful = verifyZipFile(extractedFile);
- // Log the sha1 of the extracted zip file
- Log.i(TAG, "Extraction " + (isExtractionSuccessful ? "success" : "failed") +
- " - length " + extractedFile.getAbsolutePath() + ": " +
- extractedFile.length());
- if (!isExtractionSuccessful) {
- // Delete the extracted file
- extractedFile.delete();
- if (extractedFile.exists()) {
- Log.w(TAG, "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++;
- dexFile = apk.getEntry(DEX_PREFIX + secondaryNumber + DEX_SUFFIX);
- }
- } finally {
- try {
- apk.close();
- } catch (IOException e) {
- Log.w(TAG, "Failed to close resource", e);
- }
- }
- return files;
- }
MultiDexExtractor.putStoredApkInfo方法:
[java] view plain copy
- private static void putStoredApkInfo(Context context, long timeStamp, long crc,
- int totalDexNumber) {
- SharedPreferences prefs = getMultiDexPreferences(context);
- SharedPreferences.Editor edit = prefs.edit();
- edit.putLong(KEY_TIME_STAMP, timeStamp); // 时间戳
- edit.putLong(KEY_CRC, crc); // crc值。
- /* SharedPreferences.Editor doc says that apply() and commit() "atomically performs the
- * requested modifications" it should be OK to rely on saving the dex files number (getting
- * old number value would go along with old crc and time stamp).
- */
- edit.putInt(KEY_DEX_NUMBER, totalDexNumber); // dex总个数。
- apply(edit);
- }
MultiDex.installSecondaryDexes方法,对dex进行安装:
[java] view plain copy
- private static void installSecondaryDexes(ClassLoader loader, File dexDir, List<File> files)
- throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException,
- InvocationTargetException, NoSuchMethodException, IOException {
- // 安装。
- if (!files.isEmpty()) {
- if (Build.VERSION.SDK_INT >= 19) {
- V19.install(loader, files, dexDir);
- } else if (Build.VERSION.SDK_INT >= 14) {
- V14.install(loader, files, dexDir);
- } else {
- V4.install(loader, files);
- }
- }
- }
这个方法的代码非常简单,挑选V19.install分析:
[java] view plain copy
- /**
- * 安装多dex。
- * @param loader
- * @param additionalClassPathEntries zip文件列表,这些zip文件中都只有一个文件classes.dex。
- * @param optimizedDirectory 优化的dex存放的目录。
- */
- private static void install(ClassLoader loader, List<File> additionalClassPathEntries,
- File optimizedDirectory)
- throws IllegalArgumentException, IllegalAccessException,
- NoSuchFieldException, InvocationTargetException, NoSuchMethodException {
- // 被打补丁的类加载器应该直接或间接继承BaseDexClassLoader。
- // 我们修改DexPathList pathList字段,追加额外的DEX文件项。
- /* The patched class loader is expected to be a descendant of
- * dalvik.system.BaseDexClassLoader. We modify its
- * dalvik.system.DexPathList pathList field to append additional DEX
- * file entries.
- */
- // dexPathList = load.pathList;
- Field pathListField = findField(loader, "pathList");
- Object dexPathList = pathListField.get(loader);
- ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
- // makeDexElements方法调用了DexPathList中的makeDexElements方法,这个方法可以加载并优化dex、zip、jar。
- // expandFieldArray方法将makeDexElements返回的数组patch到dexPathList.dexElements中。
- expandFieldArray(dexPathList, "dexElements", makeDexElements(dexPathList,
- new ArrayList<File>(additionalClassPathEntries), optimizedDirectory,
- suppressedExceptions));
- ......
- }
V19.makeDexElements方法:
[java] view plain copy
- /**
- * A wrapper around
- * {@code private static final dalvik.system.DexPathList#makeDexElements}.
- */
- private static Object[] makeDexElements(
- Object dexPathList, ArrayList<File> files, File optimizedDirectory,
- ArrayList<IOException> suppressedExceptions)
- throws IllegalAccessException, InvocationTargetException,
- NoSuchMethodException {
- Method makeDexElements =
- findMethod(dexPathList, "makeDexElements", ArrayList.class, File.class,
- ArrayList.class);
- // return (DexPathList.Element[]) dexPathList.makeDexElements(files, optimizedDirectory, suppressedExceptions)
- return (Object[]) makeDexElements.invoke(dexPathList, files, optimizedDirectory,
- suppressedExceptions);
- }
MultiDex.expandFieldArray方法:
[java] view plain copy
- /**
- * 原字段内容加上扩展的数组元素替换相应字段的内容,这个字段是一个数组。
- *
- * Replace the value of a field containing a non null array, by a new array containing the
- * elements of the original array plus the elements of extraElements.
- * @param instance the instance whose field is to be modified.
- * @param fieldName the field to modify.
- * @param extraElements elements to append at the end of the array.
- */
- private static void expandFieldArray(Object instance, String fieldName,
- Object[] extraElements) throws NoSuchFieldException, IllegalArgumentException,
- IllegalAccessException {
- // combined = new <Type>[original.length + extraElements.length];
- Field jlrField = findField(instance, fieldName);
- Object[] original = (Object[]) jlrField.get(instance);
- Object[] combined = (Object[]) Array.newInstance(
- original.getClass().getComponentType(), original.length + extraElements.length);
- System.arraycopy(original, 0, combined, 0, original.length);
- System.arraycopy(extraElements, 0, combined, original.length, extraElements.length);
- // 替换对象中的数组字段。
- jlrField.set(instance, combined);
- }
0 0
- 加载多个dex
- Android动态加载热更新修复功能 加载多个dex
- 【分析】多dex加载机制
- dex文件手动加载
- Dex动态加载
- 动态加载DEX
- 动态加载dex笔记
- 动态加载dex
- 动态加载dex
- android dex加载跟踪
- dex: 类加载过程
- 一个APK中包含多个dex方法
- Android动态加载jar/dex
- Android动态加载jar/dex
- Android动态加载jar/dex
- Android动态加载jar/dex
- Android动态加载jar/dex
- Android动态加载jar/dex
- spring 多数据源一致性事务方案
- 上下界网络流问题
- 排列(permutation)2_6
- MQTT协议 Websocket JS客户端
- scala学习笔记2 数组
- 加载多个dex
- 我的天 安装secutecrt 出现一系列问题
- 设计模式三-行为模式
- 构建微服务:Spring boot 入门篇
- JS利用CANVAS 画环形进度条
- 关于FPGA设计仿真和硬件实测不一致问题的讨论
- mport 做html公共模块导入操作
- Linux下格式化U盘及分区
- IOS判断是否是URL