Android应用使用Multidex突破64K方法数限制

Android应用使用Multidex突破64K方法数限制

作者:ConnorLin 2016-05-09 03:12 来源:原创 编辑:美朵

文章摘要
写在前面前几天,开发中遇到一个问题,Log信息如下:E/AndroidRuntime(10943):FATALEXCEPTION:mainE/AndroidRuntime(10943):Process:com.freeme.gallery,PID:10943E/AndroidRuntime(10943):java.lang.NoClassDefFoundError:com.freeme.gallery.data.DataManager$DateTakenComparatorE/AndroidRuntime

写在前面

前几天,开发中遇到一个问题,Log信息如下:

E/AndroidRuntime(10943): FATAL EXCEPTION: mainE/AndroidRuntime(10943): Process: com.freeme.gallery, PID: 10943E/AndroidRuntime(10943): java.lang.NoClassDefFoundError: com.freeme.gallery.data.DataManager$DateTakenComparatorE/AndroidRuntime(10943): at com.freeme.gallery.data.DataManager.<clinit>(DataManager.java:65)E/AndroidRuntime(10943): at com.freeme.gallery.app.GalleryAppImpl.getDataManager(GalleryAppImpl.java:77)E/AndroidRuntime(10943): at com.freeme.gallery.provider.GalleryProvider.onCreate(GalleryProvider.java:101)E/AndroidRuntime(10943): at android.content.ContentProvider.attachInfo(ContentProvider.java:1656)E/AndroidRuntime(10943): at android.content.ContentProvider.attachInfo(ContentProvider.java:1627)E/AndroidRuntime(10943): at android.app.ActivityThread.installProvider(ActivityThread.java:5060)E/AndroidRuntime(10943): at android.app.ActivityThread.installContentProviders(ActivityThread.java:4634)E/AndroidRuntime(10943): at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4567)E/AndroidRuntime(10943): at android.app.ActivityThread.access$1500(ActivityThread.java:153)E/AndroidRuntime(10943): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1404)E/AndroidRuntime(10943): at android.os.Handler.dispatchMessage(Handler.java:110)E/AndroidRuntime(10943): at android.os.Looper.loop(Looper.java:193)E/AndroidRuntime(10943): at android.app.ActivityThread.main(ActivityThread.java:5351)E/AndroidRuntime(10943): at java.lang.reflect.Method.invokeNative(Native Method)E/AndroidRuntime(10943): at java.lang.reflect.Method.invoke(Method.java:515)E/AndroidRuntime(10943): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:835)E/AndroidRuntime(10943): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:651)E/AndroidRuntime(10943): at dalvik.system.NativeStart.main(Native Method)

从报错信息来看,是没有找到DateTakenComparator这个内部类且又是运行时异常,那是不是和ClassLoader有关系呢?那么首先排除代码原因,开始从Gradle和Gradle插件版本入手,通过改变版本来验证。然而验证下来发现与Gradle并没关系。

那么问题到底出在哪呢?没辙!于是开始按节点排查,排查过几个关键节点后,终于得出一个结论:引入某个特定library后就会报这个错。

然而这个library是直接从Maven导入的,library本身肯定没有问题。似乎到这里线索又断了...恰逢此时,同事建议看下apk包大小。不看不知道,看过才恍然大悟,apk内大有乾坤啊。

apk包中含有两个.dex文件:classes.dex和classes2.dex,再看java.lang.NoClassDefFoundError,结果显而易见,方法数超限了!但是已经在build.gradle中配置了multiDexEnabled true和添加了android.support.multidex,为何还会出错呢? 原来是忘了继承MultiDexApplication了!敲脑袋ing...

接下来,我们借助官方文档来了解下64K方法数限制。

正文

随着应用不断增加新功能,引入新库,apk会越来越大,到达一定规模后就可能遇到方法数超限问题。早期版本错误信息如下:

Conversion to Dalvik format failed:Unable to execute dex: method ID not in [0, 0xffff]: 65536

较新版本错误信息如下:

trouble writing output:Too many field references: 131000; max is 65536.You may try using --multi-dex option.

其中数字65536是关键,Android平台的Java虚拟机Dalvik执行Dex程序时,使用的是short类型来索引DEX文件中的方法。这就意味着单个Dex文件可被引用的方法总数被限制为64x1024, 即65536。其中包括:

Android Framework的方法library的方法我们自己写的方法

为突破这个限制,需要使用multidex来生成多个dex文件。

Android5.0 (API level 21)之前版本支持Multidex

Android5.0之前使用Dalvik运行时执行应用代码,默认Dalvik限制每个apk只能有一个字节码classed.dex文件。为突破这个限制,可以使用multidex support library来管理额外的dex文件(包括代码)。

Android5.0及更高版本支持Multidex

Android5.0及更高版本使用支持从apk中加载多个dex文件的ART运行时机制,在应用安装时,加载classed(...N).dex文件并编译成一个.oat文件以支持在Android设备上运行。关于Android 5.0运行时详见ART介绍。

Note: While using Instant Run, Android Studio automatically configures your app for multidex when your app’s minSdkVersion is set to 21 or higher. Because Instant Run only works with the debug version of your app, you still need to configure your release build for multidex to avoid the 64K limit.

如果使用Instant Run,当app的minSdkVersion大于或等于21时,Android Studio会自动配置支持multidex,但是仅debug版本有效,release版仍然需要配置multidex来突破64K限制。

避免64K限制

在配置multidex之前,你或许可以通过以下方法来减小方法总数(包括引用的、library里的和自己写的方法)。

排除未使用的依赖 -此步骤通常能有效避免64K限制。使用ProGuard去除未使用的方法 -为release版本配置ProGuard,能有效排除一些无用方法

使用以上技术能有效避免更改构建配置来引用更多的方法,同时能减小apk大小,使用户消耗更少的流量。

使用Gradle配置Multidex

Android SDK Build Tools 21.1或更高版本上支持multidex,确定要配置multidex前请确保Android SDK Build Tools和Android Support Repository更新到较新版本。

通过以下步骤配置multidex:

更改Gradle配置来支持multidex修改manifest。使其支持multidexapplication类

修改模块级builde.gradle文件,修改如下:

android { compileSdkVersion 21 buildToolsVersion "21.1.0" defaultConfig { ... minSdkVersion 14 targetSdkVersion 21 ... // Enabling multidex support. multiDexEnabled true } ...}dependencies { compile ’com.android.support:multidex:1.0.0’}

在manifest文件中,添加MultidexApplication Class的引用,如下:

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android.multidex.myapplication"> <application ... android:name="android.support.multidex.MultiDexApplication"> ... </application></manifest>

通过以上步骤即可支持multidex。

Note: If your app uses extends the Application class, you can override the attachBaseContext() method and call MultiDex.install(this) to enable multidex. For more information, see the MultiDexApplication reference documentation.

如果你的应用中已经继承Application,那么可以通过复写attachBaseContext()方法并调用MultiDex.install(this)来支持multidex,即无需修改manifest文件。更多信息请看MultiDexApplication

补充:亦可直接将继承Application 改为继承MultiDexApplication,而无需修改manifest文件或复写attachBaseContext()方法。

multidex support library的使用限制

multidex support library有一些已知的限制请务必知晓,需要在应用时先行测试。

如果classes2.dex文件较大,安装dex文件到设备的数据区是一个复杂的过程,可能会导致应用程序无响应(ANR)的错误。在这种情况下,应该使用ProGuard尽量减小dex文件的大小且删除无用的代码。

在Android 4.0(API Level 14)之前,由于Dalvik linearalloc bug(问题22586),multidex可能是运行失败。如果希望运行在Level 14之前的Android系统版本,请先确保完整的测试和使用。优化代码可以减少或可能消除这些潜在的问题。

应用程序使用了multiedex配置,会造成申请很大的内存分配。可能还会引起Dalvik虚拟机的崩溃(问题78035)。此分配限制是在Android 4.0 (API level 14)上增加的,但Android5.0 (API level 21)之前的版本仍有此限制。

multidex构建工具不支持指定哪些类必须包含在首个dex文件中,因而可能导致某些library无法使用。

优化Multidex的开发和构建

multidex会加长构建应用的时间,这个必要的过程可能会拖慢你的开发进度。为加速构建过程,我们可以在Gradle中配置productFlavors: a development flavor and a production flavor.

开发时将minSdkVersion改为21使用ART运行时机制,这样能加快构建速度。release时改为合适的minSdkVersion,这样仅在release时费时较长。

build.gradle配置如下:

android { productFlavors { // Define separate dev and prod product flavors. dev { // dev utilizes minSDKVersion = 21 to allow the Android gradle plugin // to pre-dex each module and produce an APK that can be tested on // Android Lollipop without time consuming dex merging processes. minSdkVersion 21 } prod { // The actual minSdkVersion for the application. minSdkVersion 14 } } ... buildTypes { release { runProguard true proguardFiles getDefaultProguardFile(’proguard-android.txt’), ’proguard-rules.pro’ } }}dependencies { compile ’com.android.support:multidex:1.0.0’}

完成上述配置后,你可以使用结合了dev productFlavor和buildType属性的devDebug变体app。这个变体app包含如下特性:

关闭了混淆(proguard)支持multidexminSdkVersion 设置为 Android API level 21.

这些设置将使Gradle插件做如下事情:

编译应用的每个模块(包括依赖)为独立的dex文件,这个过程称为pre-dexing不作修改地include每个dex文件到apk里更重要的是,这些模块dex文件将不会合并,这样避免分割主dex文件,以加快速度

值得注意的是:上述配置后的devDebug变种app仅能运行在Android 5.0设备上。

同时,你也可以构建其他变体app,也可以在终端使用gradel命令来实现多渠道打包等。更多有关flavors和Gradle tasks信息, 请看Gradle Plugin User Guide(中文翻译).

在Android Studio中构建变种App

使用multidex时,构建变体app对管理构建过程是非常有用的。Android studio允许用户自己选择。

在Android Studio中构建变体app,步骤如下:

从左边栏打开Build Variants窗口点击build variant以选择不同变体,如图:

测试Multidex应用

测试multidex应用,需在build.gradle中配置MultiDexTestRunner:

android { defaultConfig { ... testInstrumentationRunner "com.android.test.runner.MultiDexTestRunner" }}

Note: With Android Plugin for Gradle versions lower than 1.1, you need to add the following dependency for multidex-instrumentation:

若Gradle插件版本低于1.1,你还需添加multidex-instrumentation依赖:

dependencies { androidTestCompile(’com.android.support:multidex-instrumentation:1.0.1’) { exclude group: ’com.android.support’, module: ’multidex’ }}

备注:文中链接为官方链接,请爬墙观看!

参考资料1.Building Apps with Over 64K Methods

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和据,那么Go语言可能是一个不错的选择。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值