Multidex解决方法数越界

转载来自http://xybcoder.github.io/2016/04/27/Multidex%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95%E6%95%B0%E8%B6%8A%E7%95%8C/

在Android 中单个dex文件所能包含的最大方法数为65536,这包含Android FrameWork、依赖的jar包以及应用本身代码中所有方法。65536是一个很大的数,对于一个小应用来说,它的方法数很难达到65536,但对于一些大型的应用来说,65536就很容易达到,当应用方法数超过65536时,编译器就无法完成编译工作并抛出异常:

1
2
3
4
5
6
7
8
9
10
11
12
UNEXPECTED TOP-LEVEL EXCEPTION:
    com.android.dex.DexIndexOverflowException: method ID not in [0, 0xffff]: 65536
        at com.android.dx.merge.DexMerger$6.updateIndex(DexMerger.java:502)
        at com.android.dx.merge.DexMerger$IdMerger.mergeSorted(DexMerger.java:277)
        at com.android.dx.merge.DexMerger.mergeMethodIds(DexMerger.java:491)
        at com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:168)
        at com.android.dx.merge.DexMerger.merge(DexMerger.java:189)
        at com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:454)
        at com.android.dx.command.dexer.Main.runMonoDex(Main.java:302)
        at com.android.dx.command.dexer.Main.run(Main.java:245)
        at com.android.dx.command.dexer.Main.main(Main.java:214)
        at com.android.dx.command.Main.main(Main.java:106)

那么如何解决方法数越界问题?
首先我们想到的就是删除一些无用的代码和第三方库,但是很多情况下删除无用代码,方法数还是越界,针对这个问题,网上有很多解决办法:
1.编译打包时拆分dex
原理:

  1. 核心是将一个dex拆分成main.dex和多个second.dex(我这里为了讲解方面这样取名,实际为classes.dex和classes2.dex
    …)。
  2. 使用gradle进行apk打包,同时需要依赖android-support-multidex.jar。
  3. 找出应用依赖小的第三方jar包,在build.gradle配置其编译class文件至second.dex中。这样打包完成后apk中就会出现多个dex。
  4. dalvik默认会加载main.dex,然后我们可以在main.dex的代码中手动加载second.dex至PathClassLoader。

优点: 这种方案代码改动最小,开发者工作量也小。

缺点: main.dex拆分难,往往只能缓解问题,且整个项目编译运行耗时依然长,最主要的是需要Android程序员的IDE全部切换成Android Studio或者Idea,然而对于无比偏爱Eclipse的程序员来说是难以接受的,尽管google官方宣布不再支持Eclipse。

2.开发时提前编译多个dex

原理:

  1. 第一步将项目中的代码提前生成jar包,最好是不常改动的代码。
  2. 将第三方jar包和第1步中生成的jar包,cmd执行SDK中的dx.bat命令,将jar包编译生成dex文件,比如v4.dex,v7.dex,map.dex等,最好一类功能或模块对应一个dex,然后将这些dex文件放进assets文件夹中。
  3. 将以上生成dex的jar包,做成编译环境Library。类似于android.jar,这个Library只参与主项目代码编译,不参与项目打包。这样项目就不会报错,能正常编译运行。
  4. 最后同第一种方案,手动加载assets文件中的dex文件,需要用到一个MultiDex.java文件的API。

优点: 能很大程度上解决65534的问题,且由于预先编译dex,所以项目在开发期间编译运行效率能提高很多。

缺点: 打包生成的apk体积依然庞大,且只能拆分项目里的公共模块和第三方jar包,所以对于业务和开发者庞大的项目来说,所有人在一个项目里开发协作起来依然存在问题,而且res资源文件依旧是个难以拆分的问题。

3.apk插件化动态加载
原理:

  1. 首先需要拆分项目代码,划分一个主模块和多个业务功能模块,每个模块相互独立,各个模块之间完全解耦。
  2. 主模块包含是应用的入口,需要包括主功能点和分模块的公共或依赖代码,还有一些第三方jar包或框架等(甚至能使用方案2的机制对主模块再做一个dex拆分)。
  3. 分模块在独立项目中独立编译运行,这时肯定有对主模块依赖的需求,所以同样需要一套编译环境Library的依赖库。同时分模块的res资源文件,同样拆分到分模块项目中。这样这个独立模块就能独立编译成apk文件了。
  4. 主模块中还需要一套完整的插件化框架,核心原理是使用反射实例化分模块中的Activity然后再使用一个伪Activity去代理这个实例化的Activity的生命周期和特性,对于res资源文件就需要自定义一个Context去处理了。
  5. 对于分模块apk的处理,防止在应用程序中的assets目录里也好,使用网络下载也可,不过当然是推荐后者啦!

优点: 各模块独立编译,项目拆分彻底。各模块只维护自己的代码和res文件,且支持不跨版本动态更新或fixbug,完成后几乎是程序员的春天了。

缺点: 首先最难的是项目模块的解耦和拆分,几乎是将整个项目翻了个底朝天,动作和代价非常大。其次是各子模块之间的相互调用和通信,还有一个是res文件的冲突和重复。最后是动态代理框架的编写,对技术要求非常高,从demo到成熟还需要一个不断优化的过程。

在这里详细说明第一种解决办法利用Google提供的android-support-multidex.jar这个jar包,它可以从apk中加载多个dex文件,从Android5.0以后,Android默认支持了multidex,Multidex方案主要是针对AndroidStudio和Gradle编译环境的。

现在开始解决问题吧:
在项目app目录下的build.gradle文件中的defaultConfig中添加

1
multiDexEnabled true

转载自 http://xybcoder.github.io/2016/04/27/Multidex%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95%E6%95%B0%E8%B6%8A%E7%95%8C/

接下来在dependencies中添加multidex的依赖

1
compile 'com.android.support:multidex:1.0.0'

经过上面的配置后,还需做另一项工作,那就是在代码中加入支持multidex的功能,这个功能比较简单,有三种方案可以选。

一、在manifest文件中指定Application为MultiDexApplication,如下所示:

1
2
3
4
5
6
<application
android:name="android.support.multidex.MultiDexApplication"
android:lable="......"
android:icon="......."
.........../>
<application>

二、让应用的Application继承MultiDexApplication,如下所示:

1
2
public class TestApplication extends MultiDexApplication{
}

三、如果不想让应用的Application继承MultiDexApplication,还可以选择重写Application的attachBaseContext方法,这个方法比Application的onCreate要先执行,如下所示:

1
2
3
4
5
6
7
8
public class TestApplication extends MultiDexApplication{
@Override
protected void attachBaseContext(Context context){
    super.attachBaseContext(context);
    MultiDex.install(this);

   }
}

到这里第一种解决办法已经讲完了,第一种解决办法原理就是Gradle会在apk中打包2个或多个dex文件。

上面介绍的是multidex默认的配置,我们还可以通过build.gradle文件中一些其他配置项来定制dex文件的生成过程,在有些情况下,可能需要指定主dex文件所包含的类,这个时候可以通过–main-dex-list选项来实现这个功能。下面就是修改后的build.gradle文件,在build.gradle的里面添加了afterEvaluate区域,在afterEvaluate区域内部采用了–main-dex-list选项来指定主dex中包含的类,如下所示:

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
42
43
44
45
46
47
48
49
50
apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.2"

    defaultConfig {
        applicationId "com.xybcoder.note"
        minSdkVersion 19
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
        multiDexEnabled true
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

afterEvaluate{
    println "afterEvaluate"
    tasks.matching{
        it.name.startsWith('dex')
}.each{ dx->
    def listFile=project.rootDir.absolutePath+'/app/maindexlist.txt'
    println "root dir:"+project.rootDir.absolutePath
    println "dex task found:"+dx.name
    if(dx.additionalParameters==null){
        dx.additionalParameters=[]
    }
    dx.additionalParameters+='--multi-dex'
    dx.additionalParameters+='--main-dex-list='+listFile
    dx.additionalParameters+='--minimal-main-dex'
}
}


dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.3.0'
    compile 'com.android.support:design:23.3.0'
    compile 'com.android.support:cardview-v7:23.3.0'
    compile 'com.jakewharton:butterknife:7.0.1'
    compile files('libs/lite-orm-1.5.1.jar')
    compile 'com.android.support:multidex:1.0.0'
}

注意:
maindexlist.txt指定了一些类,所有在maindexlist.txt指定的类都会打包到主dex中。它们格式是固定的,如下格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
com/xybcoder/multidextext/MainActivity.class
com/xybcoder/multidextext/Login.class
..........

//multidex这9个类必须打包到主dex中,否则会抛出异常
android/support/multidex/MultiDex.class
android/support/multidex/MultiDexApplication.class
android/support/multidex/MultiDexExtractor.class
android/support/multidex/MultiDexExtractor$1.class
android/support/multidex/MultiDex$V4.class
android/support/multidex/MultiDex$V14.class
android/support/multidex/MultiDex$V19.class
android/support/multidex/ZipUtil.class
android/support/multidex/ZipUtil$CentralDirectory.class
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值