В чем разница между реализацией, api и компиляцией в 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'

В чем разница между ними и что мне следует использовать?




Ответы (10)


tl; dr

Просто замените:

  • compile с implementation (если вам не нужна транзитивность) или api (если вам нужна транзитивность)
  • testCompile с testImplementation
  • debugCompile с debugImplementation
  • androidTestCompile с androidTestImplementation
  • compileOnly все еще действителен. Он был добавлен в 3.0 для замены предоставленного, а не компилированного. (provided введен, когда у Gradle не было имени конфигурации для этого варианта использования и он назван в соответствии с предоставленной областью Maven.)

Это одно из критических изменений, появившихся в подключаемом модуле Android Gradle 3.0, о котором Google объявил на конференции IO17.

Конфигурация compile теперь устарела и должна быть заменена на implementation или api

Из документации Gradle:

dependencies {
    api 'commons-httpclient:commons-httpclient:3.1'
    implementation 'org.apache.commons:commons-lang3:3.5'
}

Зависимости, появляющиеся в api конфигурациях, будут транзитивно открыты потребителям библиотеки и, как таковые, появятся в пути к классам компиляции потребителей.

С другой стороны, зависимости, обнаруженные в конфигурации implementation, не будут открыты потребителям и, следовательно, не попадут в путь к классам компиляции потребителей. Это дает несколько преимуществ:

  • зависимости больше не просачиваются в путь к классам компиляции потребителей, поэтому вы никогда случайно не будете зависеть от транзитивной зависимости
  • более быстрая компиляция благодаря уменьшенному размеру пути к классам
  • меньше перекомпиляций при изменении зависимостей реализации: потребители не нуждаются в перекомпиляции
  • более чистая публикация: при использовании в сочетании с новым плагином maven-publish библиотеки Java создают файлы POM, которые точно различают то, что требуется для компиляции с библиотекой, и то, что требуется для использования библиотеки во время выполнения (другими словами, не смешайте то, что необходимо для компиляции самой библиотеки, и то, что необходимо для компиляции с библиотекой).

Конфигурация компиляции все еще существует, но ее не следует использовать, поскольку она не дает гарантий, которые предоставляют конфигурации api и implementation.


Примечание. если вы используете только библиотеку в своем модуле приложения - общий случай - вы не заметите никакой разницы.
вы увидите разницу только в том случае, если у вас есть сложный проект с модули зависят друг от друга, либо вы создаете библиотеку.

person humazed    schedule 12.06.2017
comment
Кто такие потребители? - person Suragch; 08.09.2017
comment
потребитель - это модуль, использующий библиотеку. в случае Android - это приложение Android. Я думаю, что это ясно, и я не уверен, что вы об этом просите. - person humazed; 08.09.2017
comment
Для меня это тоже прозвучало так. Но если я создаю библиотеку, я, конечно, хочу, чтобы ее API был доступен приложению. В противном случае, как разработчик приложения будет использовать мою библиотеку? Вот почему я не понимаю, что implementation скрывает зависимость. Имеет ли смысл мой вопрос? - person Suragch; 08.09.2017
comment
да, теперь это имеет смысл, если ваше приложение зависит от библиотеки x, которая сама зависит от y, z. если вы используете implementation, будет отображаться только x api, но если вы используете api y, z также будет отображаться. - person humazed; 08.09.2017
comment
Понятно! Теперь это имеет больше смысла. Вы можете добавить это объяснение в свой ответ. Это более понятно, чем цитируемая документация. - person Suragch; 08.09.2017
comment
Также замените provided на compileOnly - person Tarek360; 29.09.2017
comment
Может кто-нибудь объяснить простым языком? - person Ishaan Kumar; 25.10.2017
comment
Обратите внимание: если вы действительно создаете публичную библиотеку и публикуете ее в репозитории maven, Google не предоставляет инструментов для выполнения этой работы. Вы должны использовать плагин android-maven-publish для правильного создания POM и перевода из API / реализации Gradle в синтаксис среды выполнения / компиляции Maven. Ссылка: github.com/wupdigital/android-maven-publish - person Nilzor; 30.10.2017
comment
У меня есть уточняющий вопрос. Это решает конфликты между версиями зависимостей, если они были скрыты реализацией, но мне все равно нужно включить зависимости в app? Даже если в библиотеке, которая их использует, они есть в реализации? - person lsrom; 09.11.2017
comment
@humazed, что является заменой компиляции аромата. например devСкомпилировать в? - person Shreyash Mahajan; 13.11.2017
comment
Я не понимаю, что вы подразумеваете под компиляцией аромата, если вы имеете в виду, что у вас есть версия под названием devCompile, тогда она не связана с этим изменением, и вам не нужно ничего делать; - person humazed; 13.11.2017
comment
У меня есть отдельный вопрос, но я создавал новое приложение, которое автоматически использовало реализацию. Он всегда работал при отладке, но у многих пользователей происходил сбой приложения при запуске, как если бы библиотеки, использующие реализацию, были недоступны. У кого-нибудь еще возникла такая проблема? Я не хочу использовать реализацию, если это приведет к сбою моего приложения для конечного пользователя. пример implementation 'com.android.support.constraint:constraint-layout:1.0.2' вылетает, но не компилируется. - person Joel Page; 21.02.2018
comment
вероятно, это не связано с реализацией. Я никогда не слышал о подобной проблеме, вы можете задать отдельный вопрос для дальнейшего изучения вашей проблемы. - person humazed; 21.02.2018
comment
Спасибо за ссылку на документацию Gradle. Я хотел бы отметить еще одно важное утверждение оттуда: В качестве руководства вы должны сначала предпочесть конфигурацию implementation: утечка типов реализации потребителям напрямую приведет к ошибке компиляции потребителей, которую можно решить либо удаление типа из общедоступного API или продвижение зависимости как зависимости API. Это объясняется более подробно в другом ответе, но я думаю, было бы лучше включить это, поскольку ваш ответ в настоящее время является лучшим проголосовал один. - person Andrew T.; 10.04.2018
comment
Итак, когда мы должны использовать api вместо реализации? - person Manoj Perumarath; 16.07.2018
comment
НЕ заменяйте compile на implementation, если вы используете плагин maven-publish для публикации библиотеки в репозиторий maven. Если ваша зависимость Gradle - compile, сгенерированный pom будет иметь транзитивную зависимость области компиляции. Если ваша зависимость Gradle - это реализация, сгенерированный pom будет иметь непереходную зависимость области выполнения. - person Paulo Merson; 03.09.2018
comment
Обратите внимание: если библиотека, которую вы используете, является локальной (файл .aar в папке libs), она не будет транзитивной, несмотря на то, что вы используете api. - person Fustigador; 16.10.2018
comment
Что написал @PauloMerson. Этот ответ совершенно неверен. - person nilskp; 20.02.2019
comment
@Behnam, не могли бы вы объяснить, что не так с ответом. - person humazed; 21.02.2019
comment
@PauloMerson Проблема? Документация Gradle: конфигурации compile, testCompile, runtime и testRuntime, унаследованные от подключаемого модуля Java, по-прежнему доступны, но считаются устаревшими. Вам следует избегать их использования, поскольку они сохранены только для обратной совместимости. docs.gradle.org/current/userguide/java_library_plugin.html - person Manius; 27.04.2019
comment
@Behnam Это неправильно? См. Мой комментарий выше, в документе Gradle, на который я смотрю, говорится, что эти вещи устарели. - person Manius; 27.04.2019
comment
Но поскольку эта ссылка предназначена для плагина библиотеки Java - может быть, она устарела только для этих целей? Не кажется на 100% ясным. docs.gradle.org/current/userguide/java_library_plugin.html - person Manius; 27.04.2019
comment
@Manius Первая строка (то, что большинство людей видят первой) просила вас заменить compile на implementation. Период. Я отредактировал ответ, указав заменить компиляцию на implementation (если вам не нужна транзитивность) или api (если вам нужна транзитивность). Надеюсь, это предотвратит неправильное поиск и замену зависимостей компиляции библиотеки реализацией, чтобы затем все отменить. - person Paulo Merson; 29.04.2019
comment
@PauloMerson Ваш комментарий звучал так, будто продолжайте использовать компиляцию, которая устарела. Это изменение выглядит неплохо. - person Manius; 29.04.2019
comment
как насчет «группы компиляции»? Чем мне это заменить? - person Jake; 05.08.2019
comment
ПРИМЕЧАНИЕ: вам необходимо добавить плагин java-library, иначе api не будет работать - person Topera; 26.12.2019
comment
Что мне использовать, если мне нужно время выполнения и время компиляции? В настоящее время у меня есть runtime project(:'somProject'), за которым следует implementation project(':someProject'). Использование только implementation вызывает NoClassDefFoundError. - person Maroun; 28.03.2020
comment
compile отличается от implementation. отредактируйте свой ответ - person Ramin eghbalian; 20.07.2020
comment
Я знаю об этом и упоминаю об этом в ответе, а в комментариях пояснил разницу. любые правки, улучшающие ответ, приветствуются. - person humazed; 20.07.2020

Этот ответ продемонстрирует разницу между implementation, api и compile в проекте.


Допустим, у меня есть проект с тремя модулями Gradle:

  • app (приложение для Android)
  • myandroidlibrary (библиотека Android)
  • myjavalibrary (библиотека Java)

app имеет myandroidlibrary в качестве зависимостей. myandroidlibrary имеет myjavalibrary в качестве зависимостей.

Dependency1

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 нужно использовать :myandroidlibrary, поэтому в app build.gradle используйте implementation.

(Примечание: вы также можете использовать api / compile. Но задумайтесь на мгновение.)

dependencies {
    implementation project(':myandroidlibrary')      
}

Dependency2

Как вы думаете, как должен выглядеть myandroidlibrary build.gradle? Какой объем нам следует использовать?

У нас есть три варианта:

dependencies {
    // Option #1
    implementation project(':myjavalibrary') 
    // Option #2
    compile project(':myjavalibrary')      
    // Option #3
    api project(':myjavalibrary')           
}

Dependency3

В чем разница между ними и что мне следует использовать?

Компиляция или API (вариант №2 или №3)  Dependency4

Если вы используете compile или api. Наше приложение для Android теперь может получить доступ к myandroidcomponent зависимости, которая является MySecret классом.

TextView textView = findViewById(R.id.text_view);
textView.setText(MyAndroidComponent.getComponent());
// You can access MySecret
textView.setText(MySecret.getSecret());

Реализация (вариант №1)

Dependency5

Если вы используете конфигурацию 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

person aldok    schedule 22.01.2018
comment
Я добавил зависимость к одному файлу jar, используя реализацию, если он не предоставляет доступ к нему, почему я все еще могу получить, и мой код работает нормально? - person KumailR; 02.04.2018
comment
Реализация @ smkrn110 откроет вашу библиотеку jar, но не библиотеки зависимостей jar. - person aldok; 03.04.2018
comment
так нет ли разницы между компиляцией и api? - person Wijay Sharma; 06.04.2018
comment
@WijaySharma принятый ответ гласит, что compile не гарантирует того же, что гарантирует api. - person Sub 6 Resources; 10.04.2018
comment
Обнаружен java.lang.NoClassDefFoundError: Ошибка разрешения: L ‹package› / MySecret при добавлении зависимости myandroidlibrary как .jar в приложение. - person Mable John; 13.06.2018
comment
@young, что ожидается, если вы не используете удаленное управление зависимостями. См. Этот ответ groups.google.com/forum/#! msg / adt-dev / 0ZAP8AVUZVw / W25OGYgwTngJ. У меня также есть подробный ответ на это сообщение SO stackoverflow.com/a/48434752/1760984 - person aldok; 18.06.2018
comment
Я думаю, это должен быть принятый ответ. Хорошо объяснил! - person Shashank Kapsime; 20.12.2018
comment
Он простой и интуитивно понятный, когда я его читаю, для меня это непросто и интуитивно. Пожалуйста, поясните, почему должна быть реализована myandroidlibrary. - person Marian Paździoch; 19.03.2019
comment
@ MarianPaździoch, спасибо за отзыв, я обновил ответ. Чтобы ответить на ваш вопрос: не имеет значения, какая область зависимости app будет использоваться для myandroidlibrary, вы можете использовать api / compile или реализацию. Потому что в этом примере app не нужно раскрывать свою зависимость. В отличие от модуля myandroidlibrary, который раскрывает свою зависимость от app. - person aldok; 19.03.2019
comment
@ StevenW.Klassen, это самый незаслуженный отрицательный голос, о котором я когда-либо слышал. Если вы считаете, что порядок отображения информации не является оптимальным, предложите исправление вместо того, чтобы жаловаться на него. - person Tim; 14.05.2019
comment
Если я добавлю реализацию myJavaLibrary в build.gradle myAndroidLibrary и реализацию / compile / api myAndroidLibray в build.gradle приложения, будет ли это работать? Предположим, он не оптимизирован. Просто пытаюсь понять - person Satish Patro; 10.06.2019

Конфигурация Compile устарела и должна быть заменена на implementation или api.

Вы можете прочитать документацию в разделе разделение API и реализации .

Краткая часть -

Ключевое различие между стандартным подключаемым модулем Java и подключаемым модулем библиотеки Java состоит в том, что последний представляет концепцию API, доступного потребителям. Библиотека - это компонент Java, предназначенный для использования другими компонентами. Это очень распространенный вариант использования в многопроектных сборках, но также и при наличии внешних зависимостей.

Плагин предоставляет две конфигурации, которые можно использовать для объявления зависимостей: API и реализация. Конфигурация api должна использоваться для объявления зависимостей, которые экспортируются библиотечным API, тогда как конфигурация реализации должна использоваться для объявления зависимостей, которые являются внутренними для компонента.

Для дальнейшего объяснения обратитесь к этому изображению. Краткое объяснение

person Rishav    schedule 12.06.2017

Краткое решение:

Лучше всего заменить все 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 plugin 3.0: у нас была большая проблема: одно изменение кода приводило к перекомпиляции всех модулей. Основная причина этого в том, что Gradle не знает, пропускаете ли вы интерфейс одного модуля через другой или нет.

После подключаемого модуля Android Gradle 3.0: последний подключаемый модуль Android Gradle теперь требует, чтобы вы явно указали, нет ли утечки интерфейса модуля. Исходя из этого, он может сделать правильный выбор, что следует перекомпилировать.

Таким образом, зависимость compile устарела и заменена двумя новыми:

  • api: вы пропускаете интерфейс этого модуля через свой собственный интерфейс, что означает то же самое, что и старая compile зависимость

  • implementation: вы используете этот модуль только для внутренних целей и не пропускаете его через свой интерфейс.

Итак, теперь вы можете явно указать Gradle перекомпилировать модуль, если интерфейс используемого модуля изменится или нет.

Любезно предоставлено блогом Йеруна Молса

person Shayan Amani    schedule 07.01.2018
comment
Четкое и лаконичное объяснение. Спасибо! - person LeOn - Han Li; 27.01.2019

Конфигурация зависимости Gradle

Gradle 3.0 внес следующие изменения:

  • compile -> api

    api ключевое слово совпадает с устаревшим compile, которое раскрывает эту зависимость для всех уровней.

  • compile -> implementation

    Это предпочтительный способ, потому что имеет ряд преимуществ. implementation отображать зависимость только для на один уровень выше во время сборки (зависимость доступна во время выполнения). В результате вы получаете более быструю сборку (нет необходимости перекомпилировать потребителей, которые выше, чем на 1 уровень)

  • provided -> compileOnly

    Эта зависимость доступна только во время компиляции (зависимость недоступна во время выполнения). Эта зависимость не может быть транзитивной и быть .aar. Его можно использовать с процессором аннотации времени компиляции [About] и позволяет сократить окончательный выходной файл

  • compile -> annotationProcessor

    Очень похоже на compileOnly, но также гарантирует, что транзитивная зависимость не видна для потребителя.

  • apk -> runtimeOnly

    Зависимость недоступна во время компиляции, но доступна во время выполнения.

[тип зависимости POM]

person yoAlex5    schedule 13.04.2020
comment
Другими словами, api = public, implementation = internal и compileOnly = private - мне нужно создать такие псевдонимы для этих функций, поскольку они очень запутанные. - person t3chb0t; 22.05.2020

Краткое различие в терминологии непрофессионала:

  • Если вы работаете над интерфейсом или модулем, который обеспечивает поддержку других модулей, раскрывая элементы указанной зависимости, вам следует использовать api.
  • Если вы создаете приложение или модуль, который будет реализовывать или использовать указанную зависимость внутри, используйте «реализация».
  • 'compile' работает так же, как 'api', однако, если вы только реализуете или используете какую-либо библиотеку, 'реализация' будет работать лучше и сэкономит ваши ресурсы.

прочтите ответ @aldok для исчерпывающего примера.

person Rushabh Agarwal    schedule 23.05.2018
comment
Но дело в том, что если человек сознательно пришел сюда в поисках ответа на эти вопросы, то он все-таки не дилетант. - person Rishav; 08.02.2020

Начиная с версии 5.6.3 Gradle документация предоставляет простые практические правила. чтобы определить, следует ли заменить старую compile зависимость (или новую) зависимостью implementation или api:

  • По возможности предпочитайте implementation конфигурацию api

Это избавляет от зависимостей пути к классам компиляции потребителя. Кроме того, потребители немедленно не смогут скомпилировать, если какие-либо типы реализации случайно попадут в общедоступный API.

Итак, когда следует использовать конфигурацию api? Зависимость API - это зависимость, которая содержит по крайней мере один тип, представленный в двоичном интерфейсе библиотеки, часто называемый ее ABI (двоичный интерфейс приложения). Это включает, но не ограничивается:

  • типы, используемые в суперклассах или интерфейсах
  • типы, используемые в параметрах общедоступных методов, включая общие типы параметров (где общедоступные - это то, что видно компиляторам. Т.е., общедоступные, защищенные и закрытые члены пакета в мире Java)
  • типы, используемые в публичных полях
  • общедоступные типы аннотаций

Напротив, любой тип, который используется в следующем списке, не имеет отношения к ABI и, следовательно, должен быть объявлен как implementation зависимость:

  • типы, используемые исключительно в телах методов
  • типы, используемые исключительно в закрытых членах
  • типы, находящиеся исключительно во внутренних классах (будущие версии Gradle позволят вам объявлять, какие пакеты принадлежат общедоступному API)
person Pom12    schedule 10.12.2019

Несколько заметок перед тем, как продолжить; compile устарел, и в документации указано, что вы должны использовать реализацию, потому что компиляция будет удалена в Gradle версии 7.0. Если вы запустите свою сборку Gradle с --warning-mode all, вы увидите следующее сообщение;

Конфигурация компиляции устарела для объявления зависимостей. Это не сработает с ошибкой в ​​Gradle 7.0. Вместо этого используйте конфигурацию реализации.


Просто взглянув на изображение со страниц справки, можно понять, что это такое.

Итак, у вас есть синие прямоугольники compileClasspath и runtimeClassPath.
compileClasspath - это то, что требуется для успешной сборки при запуске gradle build. Библиотеки, которые будут присутствовать в пути к классам при компиляции, будут всеми библиотеками, которые настроены в вашей сборке gradle с использованием либо compileOnly, либо implementation.

Затем у нас есть runtimeClasspath, и это все пакеты, которые вы добавили с помощью implementation или runtimeOnly. Все эти библиотеки будут добавлены в окончательный файл сборки, который вы развернете на сервере.

Как вы также видите на изображении, если вы хотите, чтобы обе библиотеки использовались для компиляции, но вы также хотите, чтобы она была добавлена ​​в файл сборки, следует использовать implementation.

Примером runtimeOnly может быть драйвер базы данных.
Примером compileOnly может быть servlet-api.
Примером implementation может быть spring-core.

Gradle

person Nico Van Belle    schedule 21.02.2021

  • реализация: в основном мы используем конфигурацию реализации. Он скрывает внутреннюю зависимость модуля от его потребителя, чтобы избежать случайного использования любой транзитивной зависимости, следовательно, более быстрая компиляция и меньше повторной компиляции.

  • api: следует использовать очень осторожно, поскольку он пропускает путь к классам компиляции потребителя, поэтому неправильное использование api может привести к загрязнению зависимостей.

  • compileOnly: когда нам не нужны никакие зависимости во время выполнения, поскольку зависимость compileOnly не станет частью окончательной сборки. мы получим меньший размер сборки.

  • runtimeOnly: когда мы хотим изменить или поменять местами поведение библиотеки во время выполнения (в окончательной сборке).

Я создал сообщение с глубокое понимание каждого из них с помощью Рабочий пример: исходный код

https://medium.com/@gauraw.negi/how-gradle-dependency-configurations-work-underhood-e934906752e5

Конфигурации Gradle

person Gauraw Negi    schedule 13.10.2020

person    schedule
comment
Не отвечает на вопрос напрямую - person skryvets; 20.04.2019
comment
Также есть разработка только - person Hohenheimsenberg; 19.07.2019
comment
Что мне использовать, если мне нужно время выполнения и время компиляции? В настоящее время у меня есть implementation, за которым следует runtime. - person Maroun; 28.03.2020