この質問で説明されているように、ビルド システムを改善し、インクリメンタル ビルドとコンパイルを有効にしました。残念なことに、 Gradles ブログ投稿を読んで期待したほど、インクリメンタル コンパイルはビルド時間を改善しませんでした。
調査の結果、アプリの奥深くにある小さなクラスにコメントを追加しただけなのに、コードベースのほぼ全体が再構築されていることが問題であることがわかりました。実際、どのクラスに触れるかは問題ではありません。Gradles の--debug
出力を見ると、基本的に常に 476 個のクラスが再コンパイルされることがわかります。
12.51 秒で完了した 476 クラスのインクリメンタル コンパイル。
変更されたファイルの定数が完全な再コンパイルをトリガーすることは理解していpublic static
ますが (これはわずかに遅いだけです)、インクリメンタル コンパイルが実際に機能するようにクラスの依存関係を適切に分割する方法がわかりません。インクリメンタル コンパイルに影響を与えるクラスの依存関係を決定するための正確な規則は何ですか? ここでいくつかの例を読むことができましたが、(かなり標準的な) プロジェクトにはまったく当てはまらないようです。
私自身のテストのいくつかは、次の結果をもたらしました。
// 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 のインターフェイスは変更されていません。
また、インターフェース C の背後に B を「隠す」ことも試みました。これが、依存関係を分割する適切な方法であると考えました (多くの場合非常に面倒ですが)。しかし、それはまったく役に立たなかったことがわかりました。
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();
}
}
私には、この大きな依存関係の塊があるように見えます。合理的に設計されたコードベースがあると主張しても、これが合理的に変更される可能性があるかどうかはわかりません。インクリメンタル コンパイルを効果的に改善する、Android 開発者の間で一般的なベスト プラクティス、ガイドライン、またはデザイン パターンはありますか?
他の人が私たちと同じ問題を抱えているかどうか疑問に思います。そうでない場合、あなたは何をしましたか?