В чем разница подключения модуля как api и как implementation в Gradle
Что проверяют
Понимаете ли вы, как зависимость одного модуля видна другим и как это влияет на скорость сборки многомодульного Android-проекта. Следом обычно спрашивают, в каком случае без api не обойтись и как вы проверяете, что конфигурации расставлены правильно.
Как отвечать
Разница в том, попадает ли зависимость в compile classpath потребителей:
implementation: зависимость нужна модулю для работы, но наружу не публикуется. Модуль:app, подключивший:core, не видит классов изimplementation-зависимостей:coreи не может их импортировать. При этом в runtime classpath и в итоговый APK они попадают, иначе приложение падало бы;api: зависимость становится частью публичного контракта модуля и транзитивно видна всем, кто от него зависит. Имеет смысл в библиотечных модулях:java-library,com.android.library.
Правило выбора: api нужен, когда типы зависимости торчат в публичных сигнатурах. Если метод модуля :network возвращает okhttp3.Response или публичный класс наследуется от чужого, потребителю этот тип нужен для компиляции. Всё остальное implementation.
Главный аргумент про скорость: Gradle делает compilation avoidance. Если поменялся ABI зависимости, подключённой через implementation, перекомпилируется только сам модуль. Через api изменение прокатывается перекомпиляцией по всем модулям выше по графу, и в проекте из десятков модулей это минуты на каждую правку.
Проверять удобно командой ./gradlew :feature:dependencies --configuration debugCompileClasspath и плагином Dependency Analysis Gradle Plugin, который подсказывает, что перевести в implementation. Устаревший compile удалён ещё в Gradle 7.
Где спотыкаются
Говорят, что implementation-зависимость не попадает в сборку. Попадает, ограничение касается только видимости при компиляции.
Ставят api везде, «чтобы собиралось». Модули начинают неявно пользоваться чужими транзитивными библиотеками, а любая правка в базовом модуле пересобирает полпроекта.
Пользуются классом из транзитивной зависимости, не объявив её у себя. Код работает, пока соседний модуль не заменит api на implementation, и тогда сборка ломается в неожиданном месте.