作为一名Android开发者,相信你对Android方法数不能超过65K的限制应该有所耳闻,随着应用程序功能不断的丰富,总有一天你会遇到一个异常:
Conversion to Dalvik format failed:Unable toexecute dex: method ID not in [0, 0xffff]: 65536
可能有些同学会说,解决这个问题很简单,我们只需要在Project.proterty中配置一句话就Ok啦,
dex.force.jumbo=true
是的,加入了这句话,确实可以让你的应用通过编译,但是在一些2.3系统的机器上很容易出现
INSTALL_FAILED_DEXOPT异常
对于以上两个异常,我们先来分析一下原因:
1、Android系统中,一个Dex文件中存储方法id用的是short类型数据,所以导致你的dex中方法不能超过65k
2、在2.3系统之前,虚拟机内存只分配了5M
知道了原因,我们就来一个个的解决上面的问题,首先对于65k的问题,我们在应用层是无法改变android系统的结构的,所以我们无法将数据类型从short改变为int或者其他类型,也就是说一个dex中的方法数不能超过65k是我们无法逾越的鸿沟,我们只能减少一个dex中的方法数,首先最容易想到的方案就是去掉一些无用的Jar包,以及将一些属性设置为public,从而可以去掉get/set方法,这种方法只能临时解决问题,随着时间的推移,总有一天还是会出现方法数超过65k的,毕竟一个应用一般是在加功能,不会减功能。
下面我来向大家介绍两种主流的解决方案,一种是以微信为代表的,将一些功能做成插件,动态加载,另一种方案是以facebook为代表的分包方案,将一个apk中的dex文件分割成多个dex文件,然后动态的去加载dex文件。其实这两种方案的核心思想是一样的,插件是把未来要开发的新功能做成apk和dex动态加载,而分包方案是将已经完成的功能分成多个dex文件动态加载,其实我个人觉得插件方案比分包方案更好的解决了65k的问题,因为插件方案不仅能够解决65k问题,还能让我们的应用体积减小,而分包只能解决65k的问题。
关于插件开发,做成动态加载,我在很早之前一篇文章中就写过其基本思想,有兴趣的同学可以看看
《实现Android 动态加载APK(Fragment or Activity实现)》
http://blog.csdn.net/yuanzeyao/article/details/38565345
下面我们重点介绍分包机制
我们知道一个apk文件里面有一个dex文件,这个dex文件里面都是经过优化了的class文件,所谓分包,就是讲一个dex文件分成多个dex文件,这里我们约定一下,第一个dex叫做main.dex,第二个叫做second.dex,通常在分包的时候,我们需要将应用启动就需要使用的类放入到main.dex中,把不是立马就需要使用的类放入到second.dex中,对于Android系统,他只会默认加载main.dex的,second.dex对于他来说可能只是一个资源文件,它是不会主动去加载second.dex,所以我在应用启动的过程中,我们需要为second.dex创建好一个类加载器,便于我在使用second.dex中的类时,能够里面加载该类。
关于如何加载second.dex也有好多做法,用的比较多的主要有一下几种
1、最简单的做法就是使用DexClassLoader进行加载,并将该DexClassLoader的父加载器设置为PathClassLoader
2、使用DexClassLoader加载,并将DexClassLoader的父加载器设置成PathClassLoader的父加载器,将PahtClassLoader的父加载器设置成DexClassLoader,仔细品味一下1和2的区别
3、将second.dex的路径放入到PathClassLoader的加载路径中
对于第2中方案,在有一种情况下是不能使用的,比如当second.dex通过DexClassLoader加载,但是second.dex中使用了一个类,这个类在main.dex中,这个时候就会抛出类找不到的异常,所以这种方案只能拥有second.dex不会用到main.dex类的时候
以上说的都是理论,下面我们来实战一下
我这里会介绍两种方案,一种是基于gradle构建Android项目,一种是基于Ant构建Android项目
方案一:基于gradle构建Android项目,并实现分包
环境要求:AndroidStudio0.9以上,gradle插件0.14.2以上
1、如果你的工程在eclipse中,那么你需要将该工程导入到Android中,此时需要你升级adt22以上
2、打开你工程的build.gradle文件,检查gradle插件是否是0.14.2版本之后,因为0.14.2之后gradle插件才支持分包
3、打开工程下某一个Moudle的build.gradle文件,添加对android-support-multidex.jar的依赖
4、去掉第三方jar包中重复的类
5、设置虚拟机堆内存空间大小,避免在编译期间OOM
6、gradle构建项目时,貌似默认是不会将so库加入工程的,所以为了避免此种情况发生,我们需要制定so库目录,对于从eclipse转换过来的工程,还需要制定src和资源文件路径
7、如果你的项目依赖了其他库, 分别在各个库工程中加入 multiDexEnabled = true 和 jniLibs.srcDirs =['libs']两个配置即可
8、如果你的项目没有自定义Application,那么你在AndroidManifest.xml中使用MultiDexApplication即可,如果你的项目有自定义Application,并且是继承是Application,那么只需要改为继承MultiDexApplication即可,如果你的项目时继承的其他Application,那么你需要重写
attachBaseContext
-
@Override
-
protectedvoidattachBaseContext(Contextbase){
-
super.attachBaseContext(base);
-
MultiDex.install(this);
-
}
经过上述配置,你的项目应该是已经成功分包了。如果分包成功,那么你解压你的apk文件,会发现有两个dex文件,通过上述的配置过程,我们发现此方案我们无法控制哪些类在main.dex中,哪些类在second.dex中,通过此种方案配置分包,可以兼容API4-API20.其加载second.dex采用的是上述方案中的3
下面我们来看看基于Ant构建Android项目,并实现分包过程
在上述方案中,由于我们无法看到gradle构建项目的脚本,所以我们无法控制哪些类在第一个dex,哪些类在第二个dex,此方案中,我们采用Ant构建,Ant是允许用户自己定义构建方案的,比如我们可以通过自定义构建方案,将项目中某些第三方jar包放入到second.dex中,关于这个如何实现,请参考开源项目吧
https://github.com/mmin18/Dex65536.git
由于该项目加载second.dex所采用的方案是上述方案2,比如second.dex中的某些第三方jar包依赖main.dex中的某些类,这种方案就会实现,所以在此我将此方案去掉,换成了方案3,也就是将second.dex的路径设置到PathClassLoader的加载路径中
我只给出Android 4.4中的解决方案,其他系统大同小异
加载second.dex方法
-
-
-
-
-
-
-
-
-
privatestaticvoidinstall(ClassLoaderloader,List<File>additionalClassPathEntries,
-
FileoptimizedDirectory)
-
throwsIllegalArgumentException,IllegalAccessException,
-
NoSuchFieldException,InvocationTargetException,NoSuchMethodException{
-
-
-
-
-
-
-
FieldpathListField=findField(loader,"pathList");
-
ObjectdexPathList=pathListField.get(loader);
-
ArrayList<IOException>suppressedExceptions=newArrayList<IOException>();
-
-
expandFieldArray(dexPathList,"dexElements",makeDexElements(dexPathList,
-
newArrayList<File>(additionalClassPathEntries),optimizedDirectory,
-
suppressedExceptions));
-
if(suppressedExceptions.size()>0){
-
for(IOExceptione:suppressedExceptions){
-
Log.w(TAG,"ExceptioninmakeDexElement",e);
-
}
-
FieldsuppressedExceptionsField=
-
findField(loader,"dexElementsSuppressedExceptions");
-
IOException[]dexElementsSuppressedExceptions=
-
(IOException[])suppressedExceptionsField.get(loader);
-
-
if(dexElementsSuppressedExceptions==null){
-
dexElementsSuppressedExceptions=
-
suppressedExceptions.toArray(
-
newIOException[suppressedExceptions.size()]);
-
}else{
-
IOException[]combined=
-
newIOException[suppressedExceptions.size()+
-
dexElementsSuppressedExceptions.length];
-
suppressedExceptions.toArray(combined);
-
System.arraycopy(dexElementsSuppressedExceptions,0,combined,
-
suppressedExceptions.size(),dexElementsSuppressedExceptions.length);
-
dexElementsSuppressedExceptions=combined;
-
}
-
-
suppressedExceptionsField.set(loader,dexElementsSuppressedExceptions);
-
}
-
}
分包成功后,解压apk文件,进入assert文件夹,我们看到如下结构,libs.apk就是第三方jar编译后形成的dex文件
对于上面提到的第二个问题INSTALL_FAILED_DEXOPT,根本原因就是2.3版本之前dalvik虚拟机的内存只有5M,所以无论是插件方案还是分包方案在某些手机上还是会遇到该问题,毕竟我们仅仅是减少了每个dex中包的数量,但是方法总数是没有减少的,所以解决此问题的根本方法就是修改虚拟机内存至8M,这个需求在Java层是无法实现,但是可以在c层实现,具体实现流程可以参考开源项目:
https://github.com/viilaismonster/LinearAllocFix.git
至于该方法中用到的一些方法,可以到android-support-multidex.jar中找到,这里就不都贴出来了,如果那里没有写清楚,欢迎留言讨论..
分享到:
相关推荐
由于 Dalvik 虚拟机的设计限制,每个 APK 文件中的方法数不能超过 65536 个。当应用逐渐变得庞大,或者引入了大量第三方库时,很容易触及这个限制。本文将详细介绍如何使用 Eclipse 和 Gradle 来解决这一问题。 ###...
3. **多DEX支持**: 对于大型应用,可能会超过65K方法限制,这时需要启用多DEX支持。Android Studio自动处理主DEX(primary DEX)和其他辅助DEX文件,确保所有必需的类都被包含。 4. **优化DEX**: 使用ProGuard或R8...
在Android 5.0及更高版本中,ART引入了多DEX支持,允许在一个APK中包含多个DEX文件,以解决单一DEX文件方法数量的限制(64K方法限制)。在Android Studio中,当你构建应用时,Gradle会自动处理多DEX配置,生成对应的...
Android系统对每个应用程序都有限制,即Dalvik虚拟机(DEX文件)的方法引用数不能超过65536个,这被称为64K方法限制。以下是对这个知识点的详细阐述: 1. **Android应用程序结构**:Android应用程序的主要代码是以...
【安卓APK的傻瓜式反编译打包工具】是一种专为Android应用程序(APK)设计的工具,它简化了对APK文件的反编译和重新打包过程,使得开发者或者逆向工程师能够轻松地查看、修改和分析APK内部的资源与代码。这种工具...
在Android应用开发中,APK文件是应用程序的打包格式,包含了所有运行在Android设备上的代码、资源和配置信息。本文将详细介绍如何使用“apk配置信息查看工具”来深入理解APK的内容,特别是通过`AppInfo.bat`和`dex-...
价值4000K的最新安卓iOS免签封装源码可处理apk报毒,最新安卓iOS免签封装源码可处理apk报毒 内有详细搭建教程
k. Data(数据) /data/data/com.android.inputmethod.latin l. Activities(活动) LatinIMESettings/LatinIMEDebugSettings/InputLanguageSelection 例如:LatinIMESettings“android键盘设置” Multiple(normal):倍数...
### ANDROID_ANT自动编译打包签名教程 #### 一、前言 随着移动应用开发的不断深入和技术迭代,为了提高开发效率与版本发布的便捷性,自动化构建工具的应用变得尤为重要。ANT作为一款开源的Java编译工具,在Android...
2. **多DEX处理**:当应用DEX文件超过64K方法限制时,需要拆分为多个DEX。优化此过程,确保加载速度快且无错误。 3. **优化 DexSplitter**:合理设置DexSplitter策略,例如按功能模块划分DEX,可减少启动时需加载的...
15. **Multidex Support**:当应用超过65K方法限制时,Android Studio可以配置支持多 dex 文件。 16. **Instant Run**:快速部署和调试功能,允许开发者即时查看代码更改的效果。 17. **Android App Bundle**:...
**65K方法限制**:这是Android早期对单个Dex文件中可引用的方法数的限制,包括应用自身的代码、依赖库中的方法以及Android SDK的方法。一旦超过这个数量,编译过程会失败,导致应用无法正常安装。为了解决这个问题,...
在Android操作系统中,应用的安全性是通过签名机制来保障的。每个Android应用在发布时都需要进行签名,以确保其来源的可信性和应用的完整性。系统签名权限是指应用具有与系统核心服务相同级别的权限,这对于开发系统...
----------------------------------- Android 编程基础 1 封面----------------------------------- Android 编程基础 2 开放手机联盟 --Open --Open --Open --Open Handset Handset Handset Handset Alliance ...
【2048安卓源码和apk】是一个与Android应用开发相关的资源,包含了游戏"2048"的源代码和编译后的APK文件。这个资源对于学习Android编程、游戏开发以及理解2048游戏算法的开发者来说非常有价值。 在Android开发中,...
- **classesX.dex**:例如classes10.dex、classes8.dex等,这些文件表明应用使用了Dalvik Executable(DEX)格式的优化,当单个.dex文件超过64K方法限制时,Android会拆分为多个.dex文件以容纳更多方法。...
在Android开发和维护中,有时候我们需要获取APK文件的一些详细信息,例如文件包名、版本号、SDK版本等。这时,我们可以借助Android SDK中的`aapt`(Android Asset Packaging Tool)命令行工具来实现。`aapt`是一个...
这时,Android支持多个.dex文件,如classes.dex、classes2.dex等,以解决64K方法限制问题。 9. **pinyindb**:可能是拼音数据库,用于实现汉字到拼音的转换,可能在搜索或者分类功能中发挥作用。 10. **kotlin**:...
"安卓图表报表相关-androidk线.zip" 包含了用于在Android平台上实现K线图的资源,可能是开源项目或示例代码集。 K线图由四部分组成:开盘价、收盘价、最高价和最低价,通过不同的颜色和形状展示了一天、一周或一月...
ApkTool助手V2.48(ApkToolAid)是针对Android应用程序开发者和逆向工程爱好者的一款强大工具,主要用于APK文件的反编译和重新打包。ApkTool是一款开源的Android应用分析和反编译框架,由IzzySoft开发,它允许用户...