安卓:我的应用程序太大了,提示“无法执行dex:方法ID不在[0,0xffff]范围内:65536”?

24
我正在尝试将我的应用程序与Box、Dropbox和Google Drive集成。这3个服务都需要许多第三方jar文件。此外,我的应用程序已经需要一些第三方jar文件。现在当我尝试从eclipse运行我的应用程序时,我会得到以下错误:
无法执行dex:方法ID不在[0, 0xffff]范围内:65536 转换为Dalvik格式失败:无法执行dex:方法ID不在[0, 0xffff]范围内:65536
看起来这个错误是因为我的应用程序有太多的方法。我相当确定大部分这些方法都来自第三方jar文件,所以通过简化代码来解决这个问题是不现实的。我在网上找到了这两个建议。
  1. 在project.properties中添加dex.force.jumbo=true(使用adt版本21)。我已经这样做了,但仍然出现错误。

  2. 按照http://android-developers.blogspot.co.il/2011/07/custom-class-loading-in-dalvik.html所述的方式使用多个dex文件。这似乎是唯一的选择,但我不明白它如何适用于我的情况。问题在于像Drive这样的服务具有太多依赖项。这个解决方案是否需要我修改Drive源代码,在引用其依赖项时使用变形?(显然不是选项)。

  3. 使用proguard来缩小删除未使用的代码/方法。使用proguard导出我的应用程序确实可以使文档服务集成在> 4.0设备上正常工作。但是,在测试2.3设备时会抛出classnotfound错误。

因此,我希望能就这个问题得到一些建议。选项2是否适用于我的情况?还有其他解决方案吗?


这三个服务都需要一些第三方JAR包。据我所知,它们中没有一个需要任何第三方JAR包。您已经选择使用JAR包及其依赖的JAR包来访问这些服务。但是,由于所有这些服务都可以通过其他方式访问而不是基于Java的客户端,它们都具有一些底层的Web服务API。例如,在Dropbox的情况下,它是一个REST风格的API:https://www.dropbox.com/developers/core/api - CommonsWare
更准确地说,这些服务的“Android库”需要许多第三方JAR包。 - ab11
1
我的观点是,你不需要使用那些库。Python开发人员不使用那些JAR文件。Ruby开发人员不使用那些JAR文件。JavaScript开发人员不使用那些JAR文件。这些JAR文件只是为了方便你的使用,但如果它们给你带来麻烦,可以降低一层并更直接地访问它们的Web服务API。 - CommonsWare
1
我认为Proguard是你最好的选择。你需要花些时间调查为什么会出现ClassNotFound异常。你可能需要调整Proguard配置。 - JesusFreke
2
它就在顶部附近 -- 应该看起来像 method_ids_size : 36700dexdump 是 SDK 的一部分。 - fadden
显示剩余4条评论
8个回答

14

您也可以将其中一个或多个作为插件开发到您的主应用程序中,以单独的APK形式提供下载。该APK将公开一些组件,供主应用程序使用--由于我不知道您与这些服务的集成性质,因此我无法对此做出更具体的建议。您可以使用自己的签名-级别自定义<permission>来保护两个应用程序之间的通信。而且,如果使用第三方库会增加其他权限要求,您只需要在插件APK中获取这些权限,从而使主APK更小。


这是我目前看到的最现实的解决方案。我会暂时不回答这个问题,希望能得到更多反馈,但我很可能最终会采用这个方案。 - ab11
我找不到这个插件架构的示例。我想找到一些示例,展示如何检查插件是否已安装以及如何使用自定义权限进行安全通信。你能指点我吗? - ab11
@ab11:由于插件架构的本质严重依赖于插件实际执行的任务,因此很难找到通用的示例。这是一对项目的示例,通过从插件传递“RemoteViews”到主机来实现插件UI:https://github.com/commonsguy/cw-omnibus/tree/master/RemoteViews Roman Nurik的DashClock显示支持插件的应用程序小部件/锁屏小部件:https://code.google.com/p/dashclock/ - CommonsWare

7

***新的**** 所有其他答案现在已过时。这是新的解决方案

Android 5.0及以上版本

多 dex 支持已自动包含。来自文档:

Android 5.0及以上版本使用名为ART的运行时,原生支持从应用APK文件加载多个dex文件。ART在应用安装时执行预编译,扫描classes(..N).dex文件并将其编译成单个.oat文件以供Android设备执行。有关Android 5.0运行时的更多信息,请参见Introducing ART。

Android 5.0以下版本

只需将 Android 多 dex 支持工具添加到 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'
}

6
Dalvik虚拟机每个dex文件最多可以有65536个方法,这是因为字节码指令集没有一种方法来引用需要超过16位的方法号(如评论中@danfuzz所指出的)。尽管可以通过使用多个dex文件来解决此问题,但Facebook发现了另一种解决方案,他们可以在其应用程序内部部署以避免问题。请参考这里

经过多次阅读,我仍然不清楚如何实现他们的解决方案。我需要向我的应用程序添加一些内容来更改内部dalvik缓冲区吗? - ab11
@ab11 你需要使用android-ndk制作一个本地扩展,以增加本地缓冲区的大小。 - Raghav Sood
@ab11 很抱歉,我在这方面无法提供太多实际帮助。我了解理论,但我自己从未遇到过这个问题,所以无法确定。 - Raghav Sood
5
注意:限制不是“内存分配问题”,而是字节码指令集没有办法引用需要超过16位的方法编号。当我离开安卓团队时,我正在解决这个问题,但似乎在这段时间里没有人接手处理它(唉)。 - danfuzz
1
Facebook的问题实际上是一个内存分配问题,与16位方法引用问题不同。需要特别注意的是,他们的修复仅适用于旧版本的Android(froyo / gingerbread),对honeycomb及以后的版本没有任何作用。16位限制同样适用于所有过去和现在的版本。 - fadden
显示剩余4条评论

5
请参阅vm/LinearAlloc.c,您可以找到以下代码:(根据我的调查,在Android 4.0之后为8MiB,在Android 2.3.3下为5MiB)
#define DEFAULT_MAX_LENGTH(5*1024*1024)
...
LinearAllocHdr *pHdr;
... pHdr->mapLength = DEFAULT_MAX_LENGTH;
我认为“Facebook修复程序”是通过使用本地C指针编辑此内存来解决问题。在我看来,LinearAlloc问题和此方法ID问题是不同的事情。

3

最近我遇到了这个问题。在搜索了更详细的实现方法后,我意识到除了以下内容外没有太多可用的资料:

我意识到问题并不一定是我的代码中有太多方法,而是我的代码和其他库的完整 dex 是问题所在。因此,如果我可以编译我的代码针对库,但不包括它们在 classes.dex 中,然后分别 dex 库,最后在运行时将所有内容组合起来,这应该可以解决问题。唯一剩下要解决的问题是类加载器,Facebook 顺便提到了它。
因此,通过一些思考和一些 Groovy 代码,我认为我想出了一个相对稳定的方法,将库和应用程序代码打包成单独的 dex 文件。

https://gist.github.com/nickcaballero/7045993


我会看一下,但是想告诉你我的可怕解决方案。我有一些外部库只用于某些功能;在开发中,除非我正在处理这些特定的功能,否则我不需要将它们包含到我的应用程序中。因此,我将它们从 libs 文件夹中删除,以便它们不被构建到应用程序中,并将它们放在类路径上,以便我的代码编译;显然,如果使用了那些功能,我的应用程序就会崩溃,但我会将所需的功能重新放回去进行开发。当我发布构建时,我会包含这些库,因为 proguard 将从库中删除未使用的类。 - ab11

1
大多数超过65k方法限制的问题与在应用程序中使用庞大的Google Play服务有关。最近,在使用它时可以获得更多细节。
按照此指南,您可以仅使用所需的部分。这可能会解决问题,避免使用一些黑魔法技巧或使用multiDex。例如,如果您只想在应用程序中使用Google Maps(而不是使用广告、钱包、Google Wear、分析等),则使用整个依赖项是浪费时间/空间的。您可以这样使用它:
compile com.google.android.gms:play-services-base:6.5.87
compile com.google.android.gms:play-services-maps:6.5.87

您可以在此链接中阅读完整的“零件”列表。


0

你需要启用Dex支持。因此,您需要执行以下步骤:

  1. Android的Gradle插件v0.14.0添加了对多Dex的支持。要启用它,您只需在build.gradle中声明即可:
android {
   defaultConfig {
      ...
      multiDexEnabled = true
   }
}
如果应用支持 > 5.0(即您的minSdkVersion为20或以下),您还需要动态修补应用程序的ClassLoader,以便它能够从次要dex文件加载类。为此,您可以添加此库。
 dependencies {
      ...
      compile 'com.android.support:multidex:1.0.0'
    }
  1. 在代码中启用,你有以下选项,请选择最适合你的一种

A. 在清单文件中添加MultiDexApplication

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.multidex.App"> <application
android:name="android.support.multidex.MultiDexApplication">
</application>
</manifest>

B. 通过MultiDexApplication扩展应用程序

public class App extends MultiDexApplication { .. }

将其安装在附加基本上下文的应用程序中。

public class App {
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        MultiDex.install(this);
        ..
    }

}

更多信息请查看此链接MultiDex


0

这里是我为了计算特定文件夹中每个jar包中方法数量(以及总数)而编写的脚本。

一旦您计算出方法数量,就可以集中精力进行重构和删除冗余库。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接