В чем разница между реализацией и компиляцией в Gradle?

1031

После обновления до 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'

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

humazed
источник

Ответы:

1283

ТЛ; др

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

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


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

humazed
источник
137
Кто такие «потребители»?
Сурагч,
34
потребитель - модуль, использующий библиотеку. в случае с Android это приложение для Android. Я думаю, что это ясно, и я не уверен, что это то, что вы просите.
сумасшедший
21
Вот как это звучало и для меня тоже. Но если я делаю библиотеку, конечно, я хочу, чтобы ее API был открыт для приложения. Иначе, как разработчик приложения будет использовать мою библиотеку? Вот почему я не понимаю смысла implementationскрывать зависимость. Мой вопрос имеет смысл?
Сурагч
235
да, это имеет смысл, если ваше приложение зависит от библиотеки x, которая сама зависит от y, z. если вы используете implementationтолько x api будет отображаться, но если вы используете apiy, z также будет отображаться.
сумасшедший
36
Понял! Это имеет больше смысла сейчас. Вы можете добавить это объяснение в свой ответ. Это более понятно, чем цитируемая документация.
Сурагч
380

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

aldok
источник
1
Я добавил зависимость к одному jar-файлу, используя реализацию, если он не предоставляет доступ к нему, почему я все еще могу получить и мой код работает нормально?
smkrn110
Реализация @ smkrn110 представит вашу библиотеку jar, но не ваши библиотеки зависимостей jar.
Алдок
2
@WijaySharma принятый ответ утверждает, что compileне гарантирует то же самое, что apiгарантирует.
Sub 6 Resources
9
Я думаю, что это должен быть принятый ответ. Хорошо объяснил!
Шашанк Капсиме
9
@ StevenW.Klassen, это самый незаслуженный голос, о котором я когда-либо слышал. Если вы считаете, что порядок информации не оптимален, предложите изменить, а не жаловаться на это
Тим
65

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

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

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

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

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

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

Rishav
источник
46

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

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

Шаян Амани
источник
2
Чистое и краткое объяснение. Спасибо!
Леон - Хан Ли
20
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| 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.  |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
Ваджид Али
источник
Непосредственно на вопрос не отвечает
скрывец
1
Существует также развитие только
Хоэнхаймсенберг
Что я должен использовать, если мне нужно время выполнения и время компиляции? В настоящее время я implementationпоследовал за runtime.
Maroun
8

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

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

Прочитайте ответ @aldok для подробного примера.

Рашабх Агарвал
источник
Но дело в том, что если человек сознательно пришел сюда в поисках ответа на эти вопросы, то он все-таки не мирянин.
Ришав
7

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

  • compile -> api

    api Ключевое слово совпадает с устаревшим compile

  • compile -> implementation

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

  • provided -> compileOnly

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

  • compile -> annotationProcessor

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

  • apk -> runtimeOnly

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

yoAlex5
источник
1
Итак, другими словами, api = public, implementation = internalи compileOnly = private- мне нужно создать такие псевдонимы для этих функций , поскольку они очень запутанным.
t3chb0t
6

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

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

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

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

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

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

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