使用Gradle在分离的dex文件中拆分外部库以解决Android Dalvik 64k方法的限制

时间:2014-05-12 16:21:36

标签: android gradle dalvik

使用Gradle是否有proper/easy方法来解决64k方法限制?

我的意思是使用pre-dexed jar来创建单独的dex文件而不是单个classes.dex的一些自定义Gradle任务。

谢谢

伊万

当前状态

目前,我正在与GMS斗争:它引入了20k种方法来使用Google Analytics。我使用Proguard去除了不需要的东西,但仍然...... 72k方法和计数......

我可以使用classes.dex参数 - multi-dex dx拆分为两个文件。我实现了手动编辑

sdk/build-tools/android-4.4W/dx

并像这样编辑最后一行:

exec java $javaOpts -jar "$jarpath" --multi-dex "$@"

我的APK文件现在包含__classes.dex__ and __classes2.dex__

我尝试使用几种方法动态加载第二个文件:

不幸的是仍然没有运气。我真的希望一些Google / Facebook / Square大师可以提供合适的解决方案。

4 个答案:

答案 0 :(得分:24)

Android Gradle插件2.2.0的更新:无法再访问dex任务,但作为{{1}的一部分,additionalParameters被引入}}。像

一样使用它
dexOptions

Android Gradle插件更新0.14.0:现在通过新的android { dexOptions { additionalParameters += '--minimal-main-dex' // additionalParameters += '--main-dex-list=$projectDir/<filename>'.toString() // additionalParameters += '--set-max-idx-number=55000' } } 指令直接支持多索引(需要build-tools 21.1.0,支持存储库版本8,和Android Studio 0.9)。

原始回答:自从Gradle Android插件0.9.0以来,您实际上可以multiDexEnabled true传递给--multi-dex,只需将其添加到您的应用中即可dx档案:

build.gradle

到目前为止,创建多个dex文件。要实际使用多个dex文件,请查看https://github.com/casidiablo/multidex(这是Google即将推出的MultiDex支持库的一个分支)。

答案 1 :(得分:8)

如果gms是你的问题并且你正在使用gradle

从gms 6.5版开始,您可以选择单独的API库

例如,仅包含Maps API:

compile 'com.google.android.gms:play-services-maps:6.5.87'

以下是完整列表:

      com.google.android.gms:play-services-base:6.5.87
      com.google.android.gms:play-services-ads:6.5.87
      com.google.android.gms:play-services-appindexing:6.5.87
      com.google.android.gms:play-services-maps:6.5.87
      com.google.android.gms:play-services-location:6.5.87
      com.google.android.gms:play-services-fitness:6.5.87
      com.google.android.gms:play-services-panorama:6.5.87
      com.google.android.gms:play-services-drive:6.5.87
      com.google.android.gms:play-services-games:6.5.87
      com.google.android.gms:play-services-wallet:6.5.87
      com.google.android.gms:play-services-identity:6.5.87
      com.google.android.gms:play-services-cast:6.5.87
      com.google.android.gms:play-services-plus:6.5.87
      com.google.android.gms:play-services-appstate:6.5.87
      com.google.android.gms:play-services-wearable:6.5.87
      com.google.android.gms:play-services-all-wear:6.5.87

答案 2 :(得分:2)

可以在此处找到分区和加载不同dex文件的示例项目:

https://code.google.com/p/android-custom-class-loading-sample/

编辑:对于Gradle,您已经有了答案

Custom Class Loading in Dalvik with Gradle (Android New Build System)

答案 3 :(得分:2)

我是https://github.com/creativepsyco/secondary-dex-gradle/的维护者,我是gradle n00b,因此我选择了BASH脚本的路径,尽管我认为它可以直接在构建文件中完成。 OR可以重构为一个插件运行,我可能会这样做,当我达到与Gradle的条款。这就是我逻辑的原因。

为了理解如何拆分DEX,您必须知道构建系统任务顺序。如果您正在使用gradle,那么您必须知道在构建周期内注入了一系列任务。

例如:

:sdk:processReleaseJavaRes UP-TO-DATE
:sdk:packageReleaseJar
:sdk:compileReleaseNdk UP-TO-DATE
:sdk:packageReleaseJniLibs UP-TO-DATE
:sdk:packageReleaseLocalJar UP-TO-DATE
:sdk:packageReleaseRenderscript UP-TO-DATE
:sdk:packageReleaseResources UP-TO-DATE
:sdk:bundleRelease
:app:prepareComAndroidSupportAppcompatV71910Library UP-TO-DATE
:app:prepareComFacebookAndroidFacebook3141Library UP-TO-DATE
:app:prepareDebugDependencies
:app:compileDebugAidl UP-TO-DATE
:app:compileDebugRenderscript UP-TO-DATE
:app:generateDebugBuildConfig UP-TO-DATE
:app:generateDebugAssets UP-TO-DATE
:app:mergeDebugAssets UP-TO-DATE
:app:generateDebugResValues UP-TO-DATE
:app:generateDebugResources UP-TO-DATE
:app:mergeDebugResources UP-TO-DATE
:app:processDebugManifest UP-TO-DATE
:app:processDebugResources UP-TO-DATE
:app:generateDebugSources UP-TO-DATE
:app:compileDebugJava
:app:preDexDebug
:app:dexDebug
:app:processDebugJavaRes UP-TO-DATE
:app:validateReleaseConfigSigning
:app:packageDebug
:app:zipalignDebug
:app:assembleDebug

为了做Dexing,您应该能够在dex *和process *任务之间注入自定义任务。如果你能做到这一点,那么Multiple DEXing变得容易了。

Bash脚本here基本上是这样做的,如果你检查调试任务,它基本上会:

  • 获取图书馆Jar文件dex,通常它是特定于构建的&amp;存在于Android Libraries&amp;的exploded-aar文件夹中在其上运行DEX工具
  • 将其复制到assets文件夹,该文件夹存在于要打包在应用程序
  • 中的最终libs文件夹中
  • 所有图书馆资源等已经合并,这意味着需要解压缩&amp; amp;再次压缩文件。

gradle build script

 // For Debug simply remove the library from getting dex and create it
                //----------------------- Extra Debug Step ----------------//
                def libraryFiles = new ArrayList<?>()
                def secondaryFile = new ArrayList<?>()

                variant.dex.libraries.each {
                    File file ->
                        if (!file.absolutePath.contains("lib/unspecified/classes.jar")) {
                            libraryFiles.add(file)
                        } else {
                            secondaryFile.add(file)
                        }
                }
                variant.dex.libraries = libraryFiles
                //----------------------- Extra Debug Step ----------------//

                packagingTask.dependsOn variant.javaCompile
            }

这会手动删除库中的dexed,以便可以通过bash脚本生成它。

我认为你可以用同样的方式在发布过程中找出dexing。另一个需要注意的重要事项是,Proguard任务由android gradle插件控制,你无法改变它。 Proguard规则问题:

  • proguard的每一次传递都不同,我们不希望在我们的2个DEX具有不同的proguard映射的情况下结束
  • 这使我们处于无法进入图书馆的情况,但这并不是理想的。
  • 必须在proguard之后生成dex文件以确保映射相同。在Proguard之后,Gradle不支持合并资产(我们希望将dex文件放在assets文件夹中)

另一个重要的代码块位于SecondaryDex.java,它基本上加载了第二个dex文件&amp;将DEX文件的路径注入运行时类路径。您可以对此进行优化,只需注入路径,而不是每次恢复应用程序时都读取DEX文件。

我在Google Play服务上进行了二级Dex实验(增加了20K方法)并且可以分成单独的DEX文件。这样我的主要dex文件就不会受到Google Play服务中膨胀的影响。

要了解Gradle任务循环的工作原理,您可以参考BasePlugin.groovy源代码,您可以看到,在有适当的API访问变体对象和构建任务之前,很难控制某些方面。 / p>