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

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

Ответ 1

ТЛ; др

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

  • compile с implementation (если вам не нужна транзитивность) или api (если вам нужна транзитивность)
  • testCompile with testImplementation
  • debugCompile with 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.


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

Ответ 2

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


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

  • приложение (приложение для 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 зависимости 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.

Ответ 3

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

Вы можете прочитать документы в https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_separation.

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

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

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

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

Ответ 4

Краткое описание:

Лучшим подходом является замена всех зависимостей 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

Ответ 5

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

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

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

Ответ 6

+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| 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.  |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+