Android Multidex 遇到的问题
来源:互联网 发布:java 多线程 线程池 编辑:程序博客网 时间:2024/06/03 15:05
Android 的classLoader在加载APK的时候限制了class.dex包含的Java方法总数不能超过65535,但是现在随便一个复杂一点的App,轻而易举就能超过65535。为了解决这个问题,google推出了官方的解决方案——Multidex
一、使用之后,相信很多人都遇到过以下几个问题:
1. Dalvik LinearAlloc Limit
安装时异常
Installation error: INSTALL_FAILED_DEXOPTPlease check logcat output for more details.Launch canceled!运行时异常
Application causes dalvik crash on gingerbread devices:LinearAlloc exceeded capacity (8388608), last=6888VM abortingFatal signal 11 (SIGSEGV) at 0xdeadd00d (code=1)
2. 首次安装启动时黑屏没有响应/ANR
3. 经常会报一些NoClassDefFoundError
二、针对这几个问题网上已经有很多讨论,现在总结一下常用的解决方案:
1、第一和第二个问题是由于Multidex 分包之后,主Dex的包过大,启动慢导致的。针对这个问题有以下解决方案:
(1).设置Multidex的分包参数,限制包的大小
a. --set-max-idx-number 用于限制每个dex的方法总数,设置为48000(经验值)可解决2.X系统上multidex导致的LinearAlloc Limit问题。
b. --minimal-main-dex 设置此参数后可让主dex的方法数尽可能的小,可以同--set-max-idx-number配合使用解决LinearAlloc Limit问题。
// hook the dex task for some additional parametersafterEvaluate { tasks.matching { it.name.startsWith('dex') }.each { dx -> if (project.android.defaultConfig.multiDexEnabled) { if (dx.additionalParameters == null) { dx.additionalParameters = [] } dx.additionalParameters += '--minimal-main-dex' // for test multidex dex , here set max idx 10000, this apk total methods is about 22251. dx.additionalParameters += '--set-max-idx-number=10000' dx.additionalParameters += '--multi-dex' } }}(2). 应用启动时显示一个欢迎页面,并且这个页面使用一个独立的init进程,目的是了过渡缓冲,让Multidex有充足的时间加载完成
<activity android:name=".activity.WelcomeActivity" android:process=":init" android:screenOrientation="portrait" > </activity>Application在非init进程中加载dex:
@Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); initProcessNameAndPackageName(base); initLog(); if (!isProcessInit()) { // other process install dex MultiDex.install(this); } else { // init process continue } }利用init进程的欢迎页,可以解决首次启动加载dex导致的黑屏和ANR问题
2、第三个问题需要定制Dex,才能解决
Multidex默认的分dex实现保证了应用内四大组件的class都在主dex中,但仍然会有NoClassXXX类型的crash出现。因为Android 加载Dex files采用的是Lazy Load,这会导致虚拟机中即使已经加载了某个class,但如果这个class不在主dex的class列表中,则主dex有可能引用不到这个class,从而导致NoClassDefFoundError。
为了解决这个问题,我们需要找出在应用启动后,虚拟机中已经加载但不在主dex中的class列表的所有class,记录到一个multidex.keep的文本文件中。关于multidex.keep文件的生成,需要在应用启动后一个合适的时机调用MultiDexUtils的getLoadedExternalDexClasses方法来手动收集:
/** * Get all loaded external classes name in "classes2.dex", "classes3.dex" .... * @param context * @return get all loaded external classes */ public List<String> getLoadedExternalDexClasses(Context context) { try { final List<String> externalDexClasses = getExternalDexClasses(context); if (externalDexClasses != null && !externalDexClasses.isEmpty()) { final ArrayList<String> classList = new ArrayList<String>(); final java.lang.reflect.Method m = ClassLoader.class.getDeclaredMethod("findLoadedClass", new Class[]{String.class}); m.setAccessible(true); final ClassLoader cl = context.getClassLoader(); for (String clazz : externalDexClasses) { if (m.invoke(cl, clazz) != null) { classList.add(clazz.replaceAll("\\.", "/").replaceAll("$", ".class")); } } return classList; } } catch (Exception e) { e.printStackTrace(); } return null; }上面手动获取了multidex.keep文件之后,接下来需要修改Gradle 编译脚本:在Gradle打包生成Dex文件之前将multidex.keep合并到主Dex中,从而保证主Dex的加载不会发生NoClassDefFoundError。
(1)首先Hook android gradle multidex list 相关 task:在createXXXMainDexClassList task之后插入一个自定义task
// hook the android gradle task : createXXXMainDexClassListtasks.whenTaskAdded { task -> android.applicationVariants.all { variant -> if (task.name == "create${variant.name.capitalize()}MainDexClassList" ) { task.finalizedBy "fix${variant.name.capitalize()}MainDexClassList" } }}
(2)在构建变种variant中加入该自定义task的声明,两个关键步骤见code中的Step1、Step2
// hook the variant to add fixXXXMainDexClassList task.android.applicationVariants.all { variant -> task "fix${variant.name.capitalize()}MainDexClassList" << { println "Fixing main dex keep file for $variant.name, while the build type is release." if (new File("${rootProject.projectDir}/buildsystem/multidex.keep").exists() && variant.buildType.name == 'release' && project.android.defaultConfig.multiDexEnabled) { File keepFile = new File("$buildDir/intermediates/multi-dex/${variant.dirName}/maindexlist.txt") // Step1 利用multidex.keep的列表找到混淆后的class name // Read proguard mapping file to find real class name in dex file def mappingList = ["key":"value"]; File mapping = new File("$buildDir/outputs/mapping/${variant.dirName}/mapping.txt") if (mapping.exists()) { mapping.eachLine { line -> if (!line.startsWith(" ") && line.endsWith(":")) { String key = line.split("->")[0].trim(); String value = line.split("->")[1].trim().split(":")[0].trim(); mappingList.put(key, value); } } } keepFile.withWriterAppend { w -> // Get a reader for the input file w.append('\n') // Step2 将对应的class list插进入multidex的构建产物maindexlist.txt 。 new File("${rootProject.projectDir}/buildsystem/multidex.keep").withReader { r -> boolean hasFindMapping = false // And write data from the input into the output mappingList.each { if (it.key.equals(r)) { r = it.value; hasFindMapping = true } } w << r << '\n' w.flush() } println "Updated main dex keep file for ${keepFile.getAbsolutePath()}" } } else { println 'There is no multidex.keep file in your project root dir or build type is debug or multidex not enabled.' } }}通过主dex的class list定制和multidex.keep文件的维护,可以解决multidex导致的启动性能问题和大部分NoClassDefFoundError Crash
三、参考文章:
Android应用打破65K方法数限制
美团Android Dex自动拆包
其实你不知道MultiDex到底有多坑
Lazy Loading Dex files
Android’s multidex slows down app startup
android-classyshark
dex-method-count工具
jadx逆向工具
- Android Multidex 遇到的问题
- 关于Android MultiDex的问题
- Android Multidex解决类过多的问题
- Android MultiDex问题
- Android Studio使用MultiDex后遇到的NoClassDefFoundError;
- multidex找不到的问题
- android MultiDex multidex原理下超出方法数的限制问题(三)
- Android中Multidex的使用
- 浅谈Android的multidex加载
- android-support-multidex解决65535问题
- android MultiDex
- Android 使用android-support-multidex解决Dex超出方法数的限制问题
- Android 使用android-support-multidex解决Dex超出方法数的限制问题
- Android 使用android-support-multidex解决Dex超出方法数的限制问题
- Android 使用android-support-multidex解决Dex超出方法数65536的限制问题
- Android 使用android-support-multidex解决Dex超出方法数的限制问题,
- Android——使用android-support-multidex解决Dex超出方法数的限制问题
- 使用android-support-multidex解决Android方法数超过65535的问题
- 【LeetCode】104. Maximum Depth of Binary Tree 求解二叉树最大深度
- ngrok原理浅析
- Activity 初始化性能优化
- canyon?最基本的二分查找
- 百度地图步行箭头和路线一致
- Android Multidex 遇到的问题
- 更改记录表CDHDR和CDPOS
- jQuery片段1.0
- 数据库SQL优化总结
- HttpClient使用讲解
- 查看当前SQL执行,包括执行状态
- Node.js 文档翻译稿
- Stopwatch 类
- 两步搞定 Tomcat 下启用 https:// 访问