В чем разница между реализацией и компиляцией в Gradle?
После обновления до Android Studio 3.0 и создания нового проекта я заметил, что в build.gradle
есть новый способ добавления новых зависимостей вместо compile
есть implementation
и вместо testCompile
есть testImplementation
,
Пример:
implementation 'com.android.support:appcompat-v7:25.0.0'
testImplementation 'junit:junit:4.12'
вместо
compile 'com.android.support:appcompat-v7:25.0.0'
testCompile 'junit:junit:4.12'
В чем разница между ними и что я должен использовать?
12 ответов
ТЛ; др
Просто замените:
compile
сimplementation
testCompile
сtestImplementation
debugCompile
сdebugImplementation
androidTestCompile
сandroidTestImplementation
compileOnly
все еще в силе. Он был добавлен в 3.0, чтобы заменить предоставленный и не компилировать. (provided
появился, когда у Gradle не было имени конфигурации для этого варианта использования, и назвал его в соответствии с предоставленной областью Maven.)
Это одно из важнейших изменений, появившихся в Gradle 3.0, о котором Google объявил на IO17.
compile
конфигурация устарела и должна быть заменена implementation
или же api
dependencies { api 'commons-httpclient:commons-httpclient:3.1' implementation 'org.apache.commons:commons-lang3:3.5' }
Зависимости, появляющиеся в
api
Конфигурации будут транзитивно предоставлены потребителям библиотеки и, как таковые, появятся на пути к классам компиляции потребителей.Зависимости, найденные в
implementation
Конфигурация, с другой стороны, не будет открыта для потребителей и, следовательно, не попадет в путь к классам компиляции потребителей. Это имеет несколько преимуществ:
- зависимости больше не попадают в путь к классам компиляции потребителей, поэтому вы никогда не будете случайно зависеть от транзитивной зависимости
- более быстрая компиляция благодаря уменьшенному размеру пути к классам
- меньше перекомпиляций при изменении зависимостей реализации: потребителям не нужно будет перекомпилировать
- Более чистая публикация: при использовании в сочетании с новым плагином maven-publish библиотеки Java создают файлы POM, которые точно различают то, что требуется для компиляции с библиотекой, и то, что требуется для использования библиотеки во время выполнения (другими словами, не смешайте то, что необходимо для компиляции самой библиотеки и что нужно для компиляции с библиотекой).
Конфигурация компиляции все еще существует, но ее не следует использовать, поскольку она не дает гарантий того, что
api
а такжеimplementation
Конфигурации предоставляют.
Примечание: если вы используете только библиотеку в модуле приложения - в общем случае - вы не заметите никакой разницы.
Вы увидите разницу, только если у вас есть сложный проект с модулями, зависящими друг от друга, или вы создаете библиотеку.
Этот ответ продемонстрирует разницу между implementation
, api
, а также compile
на проекте. Допустим, у меня есть проект с тремя модулями Gradle:
- приложение (приложение для Android)
- myandroidlibrary (андроид библиотека)
- myjavalibrary (библиотека Java)
app
имеет myandroidlibrary
как зависимости. myandroidlibrary
имеет myjavalibrary
как зависимости.
приложение -> myandroidlibrary -> myjavalibrary
myjavalibrary
имеет MySecret
учебный класс
public class MySecret {
public static String getSecret() {
return "Money";
}
}
myandroidlibrary
имеет MyAndroidComponent
класс, который манипулирует значением из MySecret
учебный класс.
public class MyAndroidComponent {
private static String component = MySecret.getSecret();
public static String getComponent() {
return "My component: " + component;
}
}
И, наконец, app
интересует только значение из myandroidlibrary
TextView tvHelloWorld = findViewById(R.id.tv_hello_world);
tvHelloWorld.setText(MyAndroidComponent.getComponent());
Теперь давайте поговорим о зависимости от app
build.gradle. Это очень просто и интуитивно понятно.
dependencies {
implementation project(':myandroidlibrary')
}
Как вы думаете myandroidlibrary
build.gradle должен выглядеть? У нас есть три варианта:
dependencies {
// Option #1
implementation project(':myjavalibrary')
// Option #2
compile project(':myjavalibrary')
// Option #3
api project(':myjavalibrary')
}
В чем разница между ними и что я должен использовать?
Компилировать и Api
Если вы используете compile
а также api
, Наше приложение для Android теперь может получить доступ myandroidcomponent
зависимость, которая является MySecret
учебный класс.
TextView textView = findViewById(R.id.text_view);
textView.setText(MyAndroidComponent.getComponent());
// You can access MySecret
textView.setText(MySecret.getSecret());
Реализация
Если вы используете implementation
конфигурации, MySecret
не выставляется.
TextView textView = findViewById(R.id.text_view);
textView.setText(MyAndroidComponent.getComponent());
// You can NOT access MySecret
textView.setText(MySecret.getSecret()); // Won't even compile
Итак, какую конфигурацию вы должны выбрать? Это действительно зависит от вашего требования.
Если вы хотите выставить зависимости, используйте api
или же compile
, если вы не хотите выставлять зависимости (скрывая свой внутренний модуль), используйте implementation
,
Это всего лишь суть конфигураций Gradle, см. Таблицу 49.1. Плагин библиотеки Java - конфигурации, используемые для объявления зависимостей для более подробного объяснения.
Пример проекта для этого ответа доступен по https://github.com/aldoKelvianto/ImplementationVsCompile
Compile
конфигурация устарела и должна быть заменена implementation
или же api
,
Вы можете прочитать документы по адресу https://docs.gradle.org/current/userguide/java_library_plugin.html.
Краткая часть
Основное различие между стандартным плагином Java и плагином Java Library заключается в том, что последний представляет концепцию API, предоставляемого потребителям. Библиотека - это компонент Java, предназначенный для использования другими компонентами. Это очень распространенный вариант использования в многопроектных сборках, но также, как только у вас есть внешние зависимости.
Плагин предоставляет две конфигурации, которые могут использоваться для объявления зависимостей: API и реализация. Конфигурация api должна использоваться для объявления зависимостей, которые экспортируются библиотечным API, тогда как конфигурация реализации должна использоваться для объявления зависимостей, которые являются внутренними для компонента.
Краткое решение:
Лучший подход - заменить все compile
зависимости с implementation
зависимостей. И только там, где у вас есть утечка интерфейса модуля, вы должны использовать api
, Это должно вызвать гораздо меньше перекомпиляции.
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:25.4.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
// …
testImplementation 'junit:junit:4.12'
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
}
Объясни подробней:
До появления плагина Android Gradle 3.0: у нас была большая проблема: изменение кода приводит к перекомпиляции всех модулей. Основная причина этого заключается в том, что Gradle не знает, пропускаете ли вы интерфейс модуля через другой или нет.
После плагина Android Gradle 3.0: последний плагин Android Gradle теперь требует от вас явного определения утечки интерфейса модуля. Исходя из этого, он может сделать правильный выбор в отношении того, что следует перекомпилировать.
Как таковой compile
Зависимость устарела и заменена двумя новыми:
api
: вы пропускаете интерфейс этого модуля через собственный интерфейс, то есть точно так же, как старыйcompile
зависимостьimplementation
: вы используете этот модуль только для внутреннего использования и не пропускаете его через интерфейс
Так что теперь вы можете явно сказать Gradle перекомпилировать модуль, если интерфейс используемого модуля изменяется или нет.
Предоставлено блогом Jeroen Mols
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| Name | Role | Consumable? | Resolveable? | Description |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| api | Declaring | no | no | This is where you should declare |
| | API | | | dependencies which are transitively |
| | dependencies | | | exported to consumers, for compile. |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| implementation | Declaring | no | no | This is where you should |
| | implementation | | | declare dependencies which are |
| | dependencies | | | purely internal and not |
| | | | | meant to be exposed to consumers. |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| compileOnly | Declaring compile | yes | yes | This is where you should |
| | only | | | declare dependencies |
| | dependencies | | | which are only required |
| | | | | at compile time, but should |
| | | | | not leak into the runtime. |
| | | | | This typically includes dependencies |
| | | | | which are shaded when found at runtime. |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| runtimeOnly | Declaring | no | no | This is where you should |
| | runtime | | | declare dependencies which |
| | dependencies | | | are only required at runtime, |
| | | | | and not at compile time. |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| testImplementation | Test dependencies | no | no | This is where you |
| | | | | should declare dependencies |
| | | | | which are used to compile tests. |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| testCompileOnly | Declaring test | yes | yes | This is where you should |
| | compile only | | | declare dependencies |
| | dependencies | | | which are only required |
| | | | | at test compile time, |
| | | | | but should not leak into the runtime. |
| | | | | This typically includes dependencies |
| | | | | which are shaded when found at runtime. |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| testRuntimeOnly | Declaring test | no | no | This is where you should |
| | runtime dependencies | | | declare dependencies which |
| | | | | are only required at test |
| | | | | runtime, and not at test compile time. |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
Gradle 3.0
внесены следующие изменения:
compile
->api
api
ключевое слово совпадает с устаревшимcompile
compile
->implementation
Это предпочтительный способ, потому что имеет ряд преимуществ.
implementation
выставлять зависимость только на один уровень выше во время сборки (зависимость доступна во время выполнения). В результате вы получаете более быструю сборку (нет необходимости перекомпилировать потребителей, которые выше, чем на 1 уровень)provided
->compileOnly
Эта зависимость доступна только во время компиляции(зависимость недоступна во время выполнения). Эта зависимость не может быть транзитивной и быть
.aar
. Его можно использовать с процессором аннотации времени компиляции и позволяет уменьшить окончательный выходной файлcompile
->annotationProcessor
Очень похоже на
compileOnly
но также гарантирует, что транзитивная зависимость не видна для потребителяapk
->runtimeOnly
Зависимость недоступна во время компиляции, но доступна во время выполнения.
Краткая разница в терминах непрофессионала:
- Если вы работаете над интерфейсом или модулем, который предоставляет поддержку другим модулям, выставляя членов указанной зависимости, вы должны использовать "api".
- Если вы создаете приложение или модуль, который собирается внедрить или использовать указанную зависимость внутри, используйте "реализацию".
- 'compile' работает так же, как и 'api', однако, если вы только внедряете или используете какую-либо библиотеку, реализация 'будет работать лучше и сэкономит ваши ресурсы.
прочитайте ответ @aldok для всестороннего примера.
Начиная с версии 5.6.3 документация Gradle предоставляет простые практические правила, позволяющие определить,compile
зависимость (или новую) следует заменить на implementation
или api
зависимость:
- Предпочитаю
implementation
конфигурация закончиласьapi
когда возможноЭто избавляет от зависимостей пути к классам компиляции потребителя. Кроме того, потребители немедленно не смогут скомпилировать, если какие-либо типы реализации случайно попадут в общедоступный API.
Итак, когда вам следует использовать
api
конфигурация? Зависимость API - это зависимость, которая содержит по крайней мере один тип, представленный в двоичном интерфейсе библиотеки, часто называемый ее ABI (двоичный интерфейс приложения). Это включает, но не ограничивается:
- типы, используемые в суперклассах или интерфейсах
- типы, используемые в параметрах общедоступных методов, включая общие типы параметров (где общедоступные - это то, что видно компиляторам. Т.е., общедоступные, защищенные и закрытые члены пакета в мире Java)
- типы, используемые в публичных полях
- общедоступные типы аннотаций
Напротив, любой тип, который используется в следующем списке, не имеет отношения к ABI, и поэтому должен быть объявлен как
implementation
зависимость:
- типы, используемые исключительно в телах методов
- типы, используемые исключительно в закрытых членах
- типы, находящиеся исключительно во внутренних классах (будущие версии Gradle позволят вам объявлять, какие пакеты принадлежат общедоступному API)
реализация: в основном мы используем конфигурацию реализации. Он скрывает внутреннюю зависимость модуля от его потребителя, чтобы избежать случайного использования любой транзитивной зависимости, следовательно, более быстрая компиляция и меньше повторной компиляции.
api: следует использовать очень осторожно, так как он пропускает путь к классам компиляции потребителя, поэтому неправильное использование api может привести к загрязнению зависимостей.
compileOnly: когда нам не нужны никакие зависимости во время выполнения, поскольку зависимость compileOnly не станет частью окончательной сборки. мы получим меньший размер сборки.
runtimeOnly: когда мы хотим изменить или поменять местами поведение библиотеки во время выполнения (в финальной сборке).
Я создал пост с глубоким пониманием каждого из них с рабочим примером: исходный код
https://medium.com/@gauraw.negi/how-gradle-dependency-configurations-work-underhood-e934906752e5
Просто взглянув на изображение со страниц справки, можно понять, что это такое.
Итак, у вас есть синие коробки и
runtimeClassPath
. В
compileClasspath
это то, что требуется для успешной сборки при запуске
gradle build
. Библиотеки, которые будут присутствовать в пути к классам при компиляции, будут всеми библиотеками, которые настроены в вашей сборке gradle с помощью либо или.
Тогда у нас есть
runtimeClasspath
и это все пакеты, которые вы добавили с помощью или. Все эти библиотеки будут добавлены в окончательный файл сборки (jar), который вы развернете на сервере.
Как вы также видите на изображении, если вы хотите, чтобы библиотека использовалась для компиляции, но вы также хотите, чтобы она была добавлена в файл сборки, тогда следует использовать.
Пример
runtimeOnly
может быть драйвером базы данных.
Пример
compileOnly
может быть servlet-api.
Пример
implementation
может быть пружинным.
Другие ответы объяснили разницу.
Просто убедитесь, что для Kotlin DSL (build.gradle.kts) функции должны иметь круглые скобки и их строковые аргументы, заключенные в двойные кавычки, а не в одинарные:
- Groovy (сборка.градле)
implementation 'com.android.support:appcompat-v7:25.0.0' testImplementation 'junit:junit:4.12'
- Котлин (build.gradle.kts)
implementation("com.android.support:appcompat-v7:25.0.0") testImplementation("junit:junit:4.12")
Когда вы объявляете зависимость в проекте Gradle, кодовая база + ее зависимости (объявленные как API) могут использоваться потребительским проектом Gradle.
Возьмем пример
У нас есть уровень 1, уровень 2, уровень 3 в качестве проектов Gradle.
уровень 1 использует уровень 2. уровень 2 использует уровень 3.
уровень 1 <- уровень 2 <- уровень 3
используя API и реализацию, мы можем контролировать, должны ли классы уровня 3 подвергаться воздействию уровня 1.
Как это ускоряет сборку:
Любые изменения уровня 3. не требуют перекомпиляции уровня 1. Особенно при разработке экономит время.