这是一个测试项目:click
我有一个测试Gradle Android项目,其中包含三个模块:app
,library_a
,library_b
。 app
取决于library_a
,然后library_a
取决于library_b
:
build.gradle(app)
dependencies {
...
compile (project(":library_a")){
transitive = false;
}
}
build.gradle(library_a)
dependencies {
...
compile (project(":library_b")){
transitive = false;
}
}
请注意,我设置了transitive = false
,因为我不希望从library_b
访问app
的课程
每个模块只有一个类,代码非常简单:
应用程式:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
//...
ClassA classA = new ClassA();
classA.doSomething();
}
}
library_a:
public class ClassA
{
public void doSomething(){
Log.i("Test", "Done A!");
ClassB classB = new ClassB();
classB.doSomething();
}
}
library_b:
public class ClassB
{
public void doSomething(){
Log.i("Test", "Done B!");
}
}
嗯,问题在于:我正在用gradlew构建我的项目。 Apk正在成功编译,但是当我运行它时,我得到NoClassDefFoundError。
I/Test﹕ Done A!
E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.NoClassDefFoundError: ru.pvolan.library_b.ClassB
at ru.pvolan.somelibrary.ClassA.doSomething(ClassA.java:12)
...
如果我在两个.gradle文件中设置transitive = true
,它运行正常,但是,正如我上面提到的,我不希望依赖是可传递的,只要我不希望可以访问ClassB来自MainActivity - 仅限ClassA。
我做错了什么?
答案 0 :(得分:1)
问题是library_b
是必需的依赖项。您不能简单地排除它,因为您需要它在运行时位于类路径上。您实际上是在歪曲您的实际依赖关系,以强制执行代码约定,从而失去利用Gradle等依赖关系管理系统的任何优势。如果你想强制执行类或包黑名单,我建议使用像PMD这样的源分析工具。这是将特定类列入黑名单的an example规则。
如果由于某种原因无法做到这一点,只需将library_b
添加到app
的运行时类路径中即可使上述示例“正常工作”。
dependencies {
runtime project(':library_b')
}
答案 1 :(得分:1)
这是Gradle在Gradle v3.4中简化的问题。
如果将库A转换为使用v3.4,则有一个简单的修复。
Gradle 3.4更改"编译"配置到一组配置" api"和"实施"。
首先你应该将gradle升级到3.4并使用java-library插件代替java插件。
你应该使用" api"在API方法调用中显式使用的任何jar上的配置(返回类型,输入参数等)。
对于您想要隐藏的所有其他罐子" (如图书馆B)您应该使用"实施"组态。由于库B仅在实现方法体内使用,因此无需在编译时将其暴露给任何其他jar;但是它仍然需要在运行时可用,因此库A可以使用它。
要实现此功能,您的Library A脚本应替换
apply plugin: 'java'
dependencies {
...
compile (project(":library_b")){
transitive = false;
}
}
与
apply plugin: 'java-library'
dependencies {
implementation project(":library_b")
}
此更改会告诉Gradle将库B包含为 app 的运行时依赖关系,以便 app 无法针对它进行编译,但是库B仍然可以在运行时供库A使用。如果出于某种原因 app 将来需要库B,则会强制在其依赖列表中明确包含库B,以确保它获得所需的版本。
有关详细信息和示例,请参阅Gradle本身的此说明: https://blog.gradle.org/incremental-compiler-avoidance
答案 2 :(得分:0)
你使用multidex吗? 当我遇到这样的问题时,我使用了multidex并从不同的模块调用了类。我只能通过关闭multidex并运行proguard来解决它。
<强> UPD 强>
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'
}
有关多dex https://developer.android.com/tools/building/multidex.html
的更多信息和关于proguard http://developer.android.com/tools/help/proguard.html