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

Из документации 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 Конфигурации предоставляют.


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

Этот ответ продемонстрирует разницу между 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. Особенно при разработке экономит время.