Gradle和嵌套的非传递依赖项

时间:2014-12-30 16:01:22

标签: android gradle android-gradle build.gradle

这是一个测试项目:click

我有一个测试Gradle Android项目,其中包含三个模块:applibrary_alibrary_bapp取决于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。

我做错了什么?

3 个答案:

答案 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