如何正确处理 class 依赖关系以在 Java/Android 和 Gradle 中利用增量编译?

How to properly deal with class dependencies to utilize incremental compilation in Java/Android with Gradle?

我改进了构建系统并激活了增量构建和编译,如 this question. To my disappointment, incremental compilation didn't improve build times as much as I expected from reading Gradles blog post 中所述。

经过一些调查,我意识到问题在于,即使我只在应用程序深处的一个小 class 地方添加评论,显然几乎整个代码库都被重建了。事实上,我触摸哪个 class 并不重要,Gradles --debug 输出显示它基本上总是重新编译 476 classes.

Incremental compilation of 476 classes completed in 12.51 secs.

虽然我知道更改文件中的 public static 常量会触发完全重新编译(只是稍微慢一点),但我不明白如何正确分解 class 依赖关系以便增量编译确实有效。确定影响增量编译的 class 依赖项的确切规则是什么?我可以阅读一些示例 here,但它似乎根本不适用于我们的(相当标准的)项目。

我自己的一些测试产生了以下结果:

// One of my main classes that has lots of class dependencies
public class A{
   public void foo() {
       // This line produces a dependency between A and B. So changing just
       // a comment in B triggers recompilation of all classes attached to A
       B b1 = new B(); 


   }
}

// A small helper class that I want to change
public class B {
   public void bar() {
       // This line does not create a dependency, so B can still be compiled by
       // itself. But usually, that's not the "common" direction you have.
       A a1 = new A();
       // I make the change here and then trigger a new build
   }
}

为什么A需要重新编译一个实现细节,而不是B的接口?

我还尝试了 "hiding" B 在接口 C 后面。我认为这将是分解依赖关系的正确方法(尽管通常很麻烦)。但事实证明它根本没有帮助。


public class A{
   public void foo() {
       C c1 = C.cFactory();
   }
}

public class B implements C {
   public void bar() {
       // I make the change here and then trigger a new build
   }
}

public interface C {
   void bar();

   public static C cFactory() {
       return new B();
   }
}

在我看来,我们有这个大的依赖性 blob,我不确定这是否可以合理地改变,即使我认为我们有一个合理设计的代码库。 Android 开发人员中是否存在可以有效改进增量编译的通用最佳实践、指南或设计模式?

我想知道其他人是否有和我们一样的问题,如果没有,你是怎么做的?

问题实际上是我们所有的 classes 都是一个大依赖图的一部分。这显示在引用

Incremental compilation of 476 classes completed in 12.51 secs.

基本上,我接触的任何 class 都会导致重新编译 476 classes。在我们的特定情况下,这是由两种模式引起的。我们在 Android、which I am not sure how to better handle 中使用显式意图。此外,我们使用 Dagger 的方式是将所有 class 连接成一个圆圈。

关于接口问题,我在实现 cFactory() 时犯了一个相当明显的错误。因为这会创建从 CB 的 class 依赖关系,从而从 A 传递到 C.

以下代码片段打破了从 AB 的依赖关系,但创建了从 BA 的依赖关系。

public class A{
   public static void foo(C input) {
       input.foo();
   }
}

public class B implements C {
   public void bar() {
       // I make the change here and then trigger a new build
       A.foo(this);
   }
}

public interface C {
   void bar();
}