是否有proper/easy
方法来解决使用Gradle的64k方法限制?
我的意思是一些自定义的Gradle任务,使用预索引的jar来创建分离的索引文件,而不是单个的classes.dex
。
谢谢伊万
现状目前,我正在努力与GMS:它带来了20000种方法来使用分析。我用Proguard去掉不需要的东西,但仍然……72k方法和计数…
我可以分割classes.dex
在两个文件使用dx
参数——multi-dex。我手工编辑
sdk/build-tools/android-4.4W/dx
和编辑最后一行:
exec java $javaOpts -jar "$jarpath" --multi-dex "$@"
我的APK文件现在包含__classes.dex__ and __classes2.dex__
.
我正在尝试用几个方法动态加载第二个文件:
- Dexdex链接
- Dexter链接
- 二级索引gradle link
可惜还是没有运气。我真的希望谷歌/Facebook/Square的专家能提供一个合适的解决方案。
Android Gradle插件2.2.0的更新:不可能再访问dex
任务了,但作为交换,additionalParameters
作为dexOptions
的一部分被引入。像
android {
dexOptions {
additionalParameters += '--minimal-main-dex'
// additionalParameters += '--main-dex-list=$projectDir/<filename>'.toString()
// additionalParameters += '--set-max-idx-number=55000'
}
}
Android Gradle插件0.14.0的更新:现在通过新的multiDexEnabled true
指令直接支持多索引(需要build-tools 21.1.0,支持repository revision 8和Android Studio 0.9)。
原来的答案:自从Gradle Android插件0.9.0你实际上可以传递--multi-dex
到dx
添加到你的应用程序的build.gradle
文件:
afterEvaluate {
tasks.matching {
it.name.startsWith('dex')
}.each { dx ->
if (dx.additionalParameters == null) {
dx.additionalParameters = ['--multi-dex']
} else {
dx.additionalParameters += '--multi-dex'
}
// Add more additional parameters like this:
dx.additionalParameters += '--main-dex-list=class-list.txt'
dx.additionalParameters += '--minimal-main-dex'
}
}
到目前为止,对于创建多个索引文件。要真正使用多个索引文件,请查看https://github.com/casidiablo/multidex(这是Google即将推出的MultiDex支持库的一个分支)。
如果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
一个项目分区和加载不同索引文件的例子可以在这里找到:
https://code.google.com/p/android-custom-class-loading-sample/编辑:对于Gradle,你已经有了答案
使用Gradle在Dalvik中自定义类加载
我是https://github.com/creativepsyco/secondary-dex-gradle/的维护者,我是一个gradle n00b,因此我选择了BASH脚本的路径,虽然我认为它可以直接在构建文件中完成。或者可以重构作为一个插件运行,我可能会这样做,当我与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
为了执行索引,您应该能够在索引*和进程*任务之间注入自定义任务。如果你能做到这一点,那么多重数据采集就变得容易了。
这里的Bash脚本基本上是这样做的,如果您检查调试任务,它基本上会:- 获取库Jar文件到索引,通常它是构建特定的&存在于
exploded-aar
文件夹中的Android Libraries &在上面运行DEX工具 - 复制到assets文件夹,它存在于最终的libs文件夹中,要打包到app 中
- 所有的库资源等都已经合并,这意味着需要解压缩&再次压缩文件。
在gradle构建脚本
// 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
}
这将手动删除库的索引,以便可以通过bash脚本生成库。
我认为你可以用同样的方法在发布过程中找出索引。另一件需要注意的重要事情是,Proguard任务是由android gradle插件控制的,你不能对它做太多改变。Proguard规则的问题:
- 每一次的proguard都是不同的,我们不想在我们的两个dex有不同的proguard映射的情况下结束
- 这使我们处于一种无法保护库的情况,但这并不是我们真正想要的。
- 必须在proguard之后生成索引文件,以确保映射是相同的。Gradle不支持在Proguard之后合并资源(我们想把索引文件放在assets文件夹中)
另一个重要的代码块位于SecondaryDex.java中,它实际上加载了第二个索引文件&将DEX文件的路径注入到运行时类路径中。你可以对它进行优化,只注入路径,而不是每次恢复应用时都读取DEX文件。
我在Google Play Services上进行了第二次Dex实验(它添加了20K方法),并能够分离成一个单独的Dex文件。这样我的主索引文件就不会受到Google Play服务膨胀的影响。
要了解Gradle任务周期是如何工作的,你可以参考BasePlugin。在groovy源代码中,您可以看到很难控制某些方面,除非有一个适当的API来访问变量对象和构建任务。