Android主流的三种解决65536问题解决方案的思考

Android应用程序随着业务需求的扩张膨胀,就会遇到这样一个问题:Conversion to Dalvik format failed:Unable toexecute dex: method ID not in [0, 0xffff]: 65536,原因是由于dalvik虚拟机的限制每个dex只支持2^16个方法数。
但是魔高一尺,道高一丈,这些问题难不倒伟大的程序员,apk分包方案应运而生,而且衍生出了多个版本实现和一些开源库。本文不去详细讲解这些方案的原理,只是对三种主流的方案做了一些优缺点分析。

方案一:编译打包时拆分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。

代表: facebook、携程、途牛等
优点: 这种方案代码改动最小,开发者工作量也小。
缺点: main.dex拆分难,往往只能缓解问题,且整个项目编译运行耗时依然长,最主要的是需要Android程序员的IDE全部切换成Android Studio或者Idea,然而对于无比偏爱Eclipse的程序员来说是难以接受的,尽管google官方宣布不再支持Eclipse。

方案二:开发时提前编译多个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资源文件依旧是个难以拆分的问题。

方案三:apk插件化动态加载

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

代表: QQ、微信、支付宝等
优点: 各模块独立编译,项目拆分彻底。各模块只维护自己的代码和res文件,且支持不跨版本动态更新或fixbug,完成后几乎是程序员的春天了。
缺点: 首先最难的是项目模块的解耦和拆分,几乎是将整个项目翻了个底朝天,动作和代价非常大。其次是各子模块之间的相互调用和通信,还有一个是res文件的冲突和重复。最后是动态代理框架的编写,对技术要求非常高,从demo到成熟还需要一个不断优化的过程。

最后,做一个总结

从技术的角度来说:个人是偏爱第三种的,插件化、动态更新、独立运行,对于一个庞大项目来说绝对是至高目标。

从实用的角度来说:个人认为第二种方案最好,容易实现、代价小、效果明显。

你可能感兴趣的:(android)