Реализация Gradle против конфигурации API
Я пытаюсь понять, в чем разница между api
а также implementation
конфигурация при построении моих зависимостей.
В документации сказано, что implementation
имеет лучшее время сборки, но, увидев этот комментарий в аналогичном вопросе, я задался вопросом, правда ли это.
Так как я не специалист в Gradle, я надеюсь, что кто-то может помочь. Я уже прочитал документацию, но мне было интересно найти простое объяснение.
8 ответов
Я думаю, что эта тема нуждается в немного большем освещении, потому что, возможно, это не так актуально для каждого разработчика.
Gradle compile
Ключевое слово устарело в пользу нового api
а также implementation
ключевые слова.
Я не буду объяснять api
потому что это то же самое, что и использование старого compile
так что если вы замените все свои compile
с api
все будет работать как всегда.
Чтобы понять implementation
Ключевое слово нам нужен пример.
ПРИМЕР
У нас есть эта библиотека называется MyLibrary
где внутри мы используем другую библиотеку под названием InternalLibrary
, Что-то вроде этого:
//internal library module
public class InternalLibrary {
public static String giveMeAString(){
return "hello";
}
}
//my library module
public class MyLibrary {
public String myString(){
return InternalLibrary.giveMeAString();
}
}
build.gradle
зависимости MyLibrary
это вот так:
dependencies {
api project(':InternalLibrary')
}
Теперь в вашем коде вы хотите использовать MyLibrary
так что вы должны иметь build.gradle
с этой зависимостью
dependencies {
api project(':MyLibrary')
}
В коде вашего приложения, с api
ключевое слово (или используя старый compile
) вы можете получить доступ к обоим MyLibrary
а также InternalLibrary
,
//so you can access the library (as it should)
MyLibrary myLib = new MyLibrary();
System.out.println(myLib.myString());
//but you can access the internal library too (and you shouldn't)
System.out.println(InternalLibrary.giveMeAString());
Таким образом, вы потенциально "пропускаете" внутреннюю реализацию чего-то, что вы не должны использовать, потому что оно не импортировано вами напрямую.
Чтобы предотвратить это, Gradle создал новый implementation
ключевое слово, так что теперь, если вы переключитесь api
в implementation
в вашем MyLibrary
dependencies {
implementation project(':InternalLibrary')
}
И в вашем приложении build.gradle
dependencies {
implementation project(':MyLibrary')
}
ты не сможешь позвонить InternalLibrary.giveMeAString()
в коде вашего приложения больше. Хотя если MyLibrary
использует api
ключевое слово для импорта InternalLibrary
, в вашем приложении вы сможете звонить InternalLibrary.giveMeAString()
без проблем, самостоятельно, если вы используете api
или же implementation
добавить MyLibrary
в ваше приложение.
Используя такую стратегию бокса, плагин Android Gradle знает, что если вы редактируете что-то в InternalLibrary
это вызовет перекомпиляцию MyLibrary
только. Это не вызовет перекомпиляцию всего вашего приложения, потому что у вас нет доступа к InternalLibrary
, Этот механизм, когда у вас много вложенных зависимостей, может значительно ускорить сборку.(Смотрите видео в конце для полного понимания)
ВЫВОДЫ
Когда вы переключаетесь на новый плагин Android Gradle 3.XX, вы должны заменить все свои
compile
сimplementation
ключевое слово (1 *). Затем попробуйте скомпилировать и протестировать ваше приложение. Если все в порядке, оставьте код как есть, если у вас есть проблемы, возможно, у вас что-то не так с вашими зависимостями, или вы использовали что-то, что сейчас является приватным и не более доступным. Предложение от инженера плагинов Gradle для Android Джерома Дочеза (1) *)Если вы управляете библиотекой, вы должны использовать
api
для каждой зависимости, которая необходима для публичного API вашей библиотеки, при использованииimplementation
для тестовых зависимостей или зависимостей, которые не должны использоваться конечными пользователями.
ССЫЛКИ(Это то же видео, разделенное для экономии времени)
Google I / O 2017 - Как ускорить сборку Gradle (ПОЛНОЕ ВИДЕО)
Google I / O 2017 - Как ускорить сборку Gradle (ТОЛЬКО ДЛЯ НОВОЙ GRADLE PLUGIN 3.0.0)
Google I / O 2017 - Как ускорить сборку Gradle (ссылка на 1 *)
Мне нравится думать о api
зависимость как публичная (видимая другими модулями) implementation
зависимость как частная (видна только этому модулю).
Обратите внимание, что в отличие от public
/private
переменные и методы, api
/implementation
зависимости не применяются во время выполнения. Это просто оптимизация во время сборки, которая позволяет Gradle
знать, какие модули нужно перекомпилировать, когда одна из зависимостей меняет свой API.
Считайте, что у вас есть app
модуль, который использует lib1
как библиотека и lib1
использования lib2
как библиотека. Что-то вроде этого: app -> lib1 -> lib2
,
Теперь при использовании api lib2
в lib1
, затем app
можно увидеть lib2
коды при использовании: api lib1
или же implementation lib1
в app
модуль.
НО при использовании implementation lib2
в lib1
, затем app
не может видеть lib2
коды.
Пожалуйста, перейдите по ссылке: Конфигурация зависимостей Android Studio доступна на официальном сайте разработчиков Android.
Внутри блока зависимостей вы можете объявить зависимость библиотеки, используя одну из нескольких различных конфигураций зависимостей (например, реализацию, показанную выше). Каждая конфигурация зависимости предоставляет Gradle разные инструкции о том, как использовать зависимость.
реализация
Gradle добавляет зависимость в путь к классам компиляции и упаковывает зависимость в выходные данные сборки. Однако, когда ваш модуль настраивает зависимость реализации, он сообщает Gradle, что вы не хотите, чтобы модуль передавал зависимость другим модулям во время компиляции. То есть зависимость доступна другим модулям только во время выполнения. Использование этой конфигурации зависимостей вместо API или компиляции (устарело) может привести к значительному улучшению времени сборки, поскольку это уменьшает количество модулей, которые система сборки должна перекомпилировать. Например, если зависимость реализации изменяет свой API, Gradle перекомпилирует только эту зависимость и модули, которые напрямую от нее зависят. Большинство модулей приложений и тестов должны использовать эту конфигурацию.
API
Gradle добавляет зависимость к пути к классам компиляции и сборке. Когда модуль включает зависимость API, он сообщает Gradle, что модуль хочет транзитивно экспортировать эту зависимость в другие модули, чтобы она была доступна им как во время выполнения, так и во время компиляции. Эта конфигурация ведет себя так же, как компиляция (которая теперь устарела), но ее следует использовать с осторожностью и только с зависимостями, которые необходимо транзитивно экспортировать другим вышестоящим потребителям. Это связано с тем, что если зависимость API изменяет свой внешний API, Gradle перекомпилирует все модули, имеющие доступ к этой зависимости во время компиляции. Таким образом, наличие большого количества зависимостей API может значительно увеличить время сборки. Если вы не хотите предоставлять API зависимости отдельному модулю, библиотечные модули должны вместо этого использовать зависимости реализации.
Еще одно техническое замечание относительно vs. Предположим, у вас есть следующие зависимости:
dependencies {
api "com.example:foo:1.0"
implementation "com.example:bar:1.0"
}
Если вы установите сгенерированный файл jar в локальном репозитории Maven (с помощью
maven-publish
плагин) вы увидите, что сгенерированный
pom.xml
файл будет выглядеть так:
<dependency>
<groupId>com.example</groupId>
<artifactId>foo</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>bar</artifactId>
<version>1.0</version>
<scope>runtime</scope>
</dependency>
Примечание:
api
был преобразован в
compile
масштабы и
implementation
- к
runtime
сфера.
Это позволяет потребителям этой библиотеки избежать зависимостей времени выполнения в пути к классам компиляции.
Давайте посмотрим на очень простой скрипт сборки для проекта на основе JVM.
plugins {
id 'java-library'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.hibernate:hibernate-core:3.6.7.Final'
api 'com.google.guava:guava:23.0'
testImplementation 'junit:junit:4.+'
}
реализация
Зависимости, необходимые для компиляции производственного источника проекта, которые не являются частью API, предоставляемого проектом. Например, в проекте используется Hibernate для реализации внутреннего уровня сохраняемости.
api
Зависимости, необходимые для компиляции производственного источника проекта, которые являются частью API, предоставляемого проектом. Например, проект использует Guava и предоставляет открытые интерфейсы с классами Guava в их сигнатурах методов.
Ответы от MatPag и dev.bmax достаточно ясны, чтобы люди поняли разные способы использования между реализацией и API. Я просто хочу сделать дополнительное объяснение с другой стороны, надеюсь помочь людям, у которых есть тот же вопрос.
Для тестирования создал два проекта:
- проект A как проект библиотеки Java с именем "frameworks-web-gradle-plugin" зависит от "org.springframework.boot:spring-boot-gradle-plugin:1.5.20.RELEASE"
- проект B зависит от проекта A реализацией com.example.frameworks.gradle:frameworks-web-gradle-plugin:0.0.1-SNAPSHOT
Описанная выше иерархия зависимостей выглядит так:
[project-b] -> [project-a] -> [spring-boot-gradle-plugin]
Затем я протестировал следующие сценарии:
Сделать проект А зависит от "org.springframework.boot: весна-загрузка Gradle-плагин: 1.5.20.RELEASE" от реализации.
Пробег
gradle dependencies
в терминале в корневом каталоге poject B , со следующим снимком экрана вывода мы видим, что 'spring-boot-gradle-plugin' появляется в дереве зависимостей runtimeClasspath, но не в compileClasspath, я думаю, именно поэтому мы не можем использовать библиотеки, объявленной с использованием реализации, просто не будет через компиляцию.Сделать проект A зависит от 'org.springframework.boot:spring-boot-gradle-plugin:1.5.20.RELEASE' от api
Пробег
gradle dependencies
снова в терминале в корневом каталоге poject B. Теперь 'spring-boot-gradle-plugin' появляется как в дереве зависимостей compileClasspath, так и в дереве зависимостей runtimeClasspath.
Существенное отличие, которое я заметил, заключается в том, что зависимость в проекте производителя / библиотеки, объявленная способом реализации, не будет отображаться в compileClasspath потребительских проектов, поэтому мы не можем использовать соответствующую библиотеку в потребительских проектах.
Теперь в есть хорошее объяснениедокументации
Конфигурация api должна использоваться для объявления зависимостей, которые экспортируются библиотечным API, тогда как конфигурация реализации должна использоваться для объявления зависимостей, которые являются внутренними для компонента.