在Android Studio中,我需要从通过Gradle(src/org/luaj/vm2/lib/jse/JavaMethod.java
)提取的包中覆盖一个特定文件(dependencies {compile 'org.luaj:luaj-jse:3.0.1'}
)。
我将文件复制到源目录中并使用完全相同的路径并对其进行了更改。这对于正在使用它的单个JUnit测试用例来说效果很好。它也看起来就像它正在为我的项目的正常编译工作(目前无法轻易确认)。
但是,当我尝试通过ProjectType =" Android Tests"的配置立即运行我的所有测试时,我得到Error:Error converting bytecode to dex:
Cause: com.android.dex.DexException: Multiple dex files define Lorg/luaj/vm2/lib/jse/JavaMethod$Overload;
。
是否需要添加到Gradle文件中的特定任务或命令,以确保项目选择本地源目录中的文件?我尝试了Copy task和sourceSets->main->java->exclude
命令,但似乎都没有用(我可能做错了)。我还尝试了"排除模块/组"指令"编译"来自this post。
运行/调试确认的非默认设置:
我所有的JUnit测试用例都在"测试"封装
任何可以解决这个问题的答案都可以。如果不是Gradle,可能是android清单或本地源文件本身。
[编辑于2016-07-24] 当我的android模拟器运行较低的API时,错误也发生在正常编译上。 API 16和19出错,但API 23没有。
答案 0 :(得分:6)
问题:链接您的应用时,链接器会找到两个版本
如何修复:告诉gradle 排除 org.luaj:luaj-jse:3.0.1:org.luaj.vm2.lib.jse.JavaMethod from building
android {
packagingOptions {
exclude '**/JavaMethod.class'
}
}
我没有用"排除类"但它适用于删除重复的gpl许可证文件a" COPYING"。
如果这"排除"不起作用你可以
答案 1 :(得分:3)
我不完全确定我理解你的问题;但是,它听起来像是一个类路径排序问题,而不是真正的文件覆盖问题。
AFAIK,gradle没有对“依赖”部分的排序做出“保证”,除此之外它是可重复的。在编译要自定义的文件版本时,要使测试/系统使用该文件,它必须在类路径中比复制的jar文件更早。
幸运的是,gradle确实允许一种相当简单的方法“预先”到类路径:
sourceSets.main.compileClasspath = file("path/to/builddir/named/classes") + sourceSets.main.compileClasspath
我对你的系统知之甚少,不能更好地定义。但是,您应该能够轻松地根据您的需求进行定制。也就是说,如果需要,您可以将'compile'更改为其他类路径(runtime,testRuntime等)之一。此外,如果这是更好的解决方案,您可以指定您构建的jar文件而不是classes目录。请记住,它可能不是最佳的,但在类路径定义中指定两次是非常无害的。
答案 2 :(得分:2)
这相当令人费解但技术上可行。然而,这不是海报所要求的单一任务:
./gradlew dependencies
进行检查)如果可以安全地假设您使用的是Linux / Mac,则可以在第3项上运行一个简单的命令行,它只使用广泛使用的命令:
mkdir newFolder ; cd newFolder ; jar xf $filename ; rm $offendingFilePath
如果您不关心自动依赖关系管理,可以使用curl下载jar文件,我相信它可以在linux和Mac上广泛使用。
curl http://somehost.com/some.jar -o some.jar
对于更强大的实现,您可以使用groovy / java代码替换这些简单的命令行。有趣的是,知道gradle可以被视为groovy的超集,这在很多方面可以说是java的超集。这意味着您可以将java / groovy代码放在gradle.build文件中的任何位置。它不干净但有效,而且只是另一种选择。
对于4,你可以有任何东西
sourceSets.main.java.srcDirs += ["newFolder/class"]
在build.gradle的根级别,或
dependencies {
. . .
compile fileTree(dir: 'newFolder', include: ['*.class'])
. . .
答案 3 :(得分:2)
这是我在Fabio的建议之后最终添加的内容:
//Get LUAJ
buildscript { dependencies { classpath 'de.undercouch:gradle-download-task:3.1.1' }}
apply plugin: 'de.undercouch.download'
task GetLuaJ {
//Configure
def JARDownloadURL='http://central.maven.org/maven2/org/luaj/luaj-jse/3.0.1/luaj-jse-3.0.1.jar' //compile 'org.luaj:luaj-jse:3.0.1'
def BaseDir="$projectDir/luaj"
def ExtractToDir='class'
def ConfirmAlreadyDownloadedFile="$BaseDir/$ExtractToDir/lua.class"
def JarFileName=JARDownloadURL.substring(JARDownloadURL.lastIndexOf('/')+1)
def ClassesToDeleteDir="$BaseDir/$ExtractToDir/org/luaj/vm2/lib/jse"
def ClassNamesToDelete=["JavaMethod", "LuajavaLib"]
//Only run if LuaJ does not already exist
if (!file(ConfirmAlreadyDownloadedFile).exists()) {
//Download and extract the source files to /luaj
println 'Setting up LuaJ' //TODO: For some reason, print statements are not working when the "copy" directive is included below
mkdir BaseDir
download {
src JARDownloadURL
dest BaseDir
}
copy {
from(zipTree("$BaseDir/$JarFileName"))
into("$BaseDir/$ExtractToDir")
}
//Remove the unneeded class files
ClassNamesToDelete=ClassNamesToDelete.join("|")
file(ClassesToDeleteDir).listFiles().each {
if(it.getPath().replace('\\', '/').matches('^.*?/(?:'+ClassNamesToDelete+')[^/]*\\.class$')) {
println "Deleting: $it"
it.delete()
}
}
}
}
我将上传一个直接与jar一起使用的版本。
答案 4 :(得分:0)
如果再得到源jar,则为另一种解决方案:
task downloadAndCopy {
def downloadDir = "${buildDir}/downloads"
def generatedSrcDir = "${buildDir}/depSrc"
copy {
from(configurations.detachedConfiguration(dependencies.add('implementation', 'xxx:source')))
file(downloadDir).mkdirs()
into(downloadDir)
}
println("downloading file into ${downloadDir}")
fileTree(downloadDir).visit { FileVisitDetails details ->
if (!details.file.name.endsWith("jar")) {
println("ignore ${details.file.name}")
return
}
println("downloaded ${details.file.name}")
def srcFiles = zipTree(details.file).matching {
include "**/*.java"
exclude "**/NeedEclude*java"
}
srcFiles.visit {FileVisitDetails sourceFile ->
println("include ${sourceFile}")
}
copy {
from(srcFiles)
into(generatedSrcDir)
}
}
}
并记得将depSrc添加到srcDirs
android {
sourceSets {
`main.java.srcDirs = ['src/main/java', "${buildDir}/depSrc"]
}
}