Как правильно обращаться с зависимостями классов, чтобы использовать инкрементную компиляцию в Java / Android с Gradle? - PullRequest
0 голосов
/ 09 февраля 2019

Я улучшил нашу систему сборки и активировал инкрементные сборки и компиляцию, как описано в этом вопросе.К моему разочарованию, инкрементная компиляция не улучшила время сборки так, как я ожидала от прочтения публикации в блоге Gradles .

После некоторого исследования я поняла, что проблема заключается в том, что, хотя я толькодобавить комментарий к небольшому классу где-то глубоко в приложении, очевидно, почти вся база кода перестроена.На самом деле, неважно, к какому классу я прикасаюсь, вывод Gradles --debug показывает, что он в основном всегда перекомпилирует 476 классов.

Инкрементная компиляция 476 классов, завершенная за 12,51 с.

Хотя я понимаю, что 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?

Я также попытался "спрятать" 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();
   }
}

Мне кажется, что у нас есть этот большой блок зависимостей, и я сомневаюсь, что это можно было бы разумно изменить, хотя я бы сказал, что у нас есть разумноразработана кодовая база.Существуют ли передовые практики, рекомендации или шаблоны проектирования, которые распространены среди разработчиков Android, которые могли бы эффективно улучшить пошаговые компиляции?

Мне интересно, есть ли у других такая же проблема, как у нас, и если нет, то что вы делали?

1 Ответ

0 голосов
/ 11 февраля 2019

Проблема в том, что все наши классы являются частью большого графа зависимостей.Это показано в цитате

Инкрементная компиляция 476 классов, выполненных за 12,51 с.

По сути, любой класс, к которому я прикасаюсь, вызывает перекомпиляцию 476 классов.В нашем конкретном случае это вызвано двумя закономерностями.Мы используем Explicit Intents в Android, , который я не уверен, как лучше обрабатывать .Кроме того, мы используем Dagger таким образом, чтобы все классы были соединены в круг.

Что касается проблематичного интерфейса, я допустил довольно очевидную ошибку с реализацией cFactory().Поскольку это создает зависимость класса от C до B и, таким образом, транзитивно от A до C.

Следующий фрагмент разбивает зависимость от A до B, но создает ее изB до A.

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();
}
...