Яка різниця між реалізацією та компіляцією в Gradle?


1027

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

Яка різниця між ними і чим я повинен користуватися?

Відповіді:


1279

тл; д-р

Просто замініть:

  • 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З іншого боку, виявлені в конфігурації залежності не піддаватимуться споживачам, а отже, не просочуватимуться до компіляційного класу споживачів. Це має ряд переваг:

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

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


Примітка. Якщо ви використовуєте лише бібліотеку в своєму модулі програми - звичайний випадок - ви не помітите жодної різниці.
ви побачите різницю лише в тому випадку, якщо у вас складний проект із модулями залежно один від одного, або ви створюєте бібліотеку.


137
Хто такі "споживачі"?
Сурагч

34
споживач - це модуль, що використовує бібліотеку. у випадку з Android - це програма Android. Я думаю, що це зрозуміло, і я не впевнений, що це те, про що ви просите.
гумав

21
Саме це мені теж звучало. Але якщо я створюю бібліотеку, я, звичайно, хочу, щоб її API піддавався додатку. В іншому випадку, як розробник програми використовувати мою бібліотеку? Ось чому я не розумію implementationприховувати залежність. Чи має моє запитання сенс?
Сурагч

235
так, зараз має сенс, якщо ваш додаток залежить від бібліотеки x, яка сама залежить від y, z. якщо ви використовуєте implementationлише x api, буде відкрито, але якщо ви використовуєте apiy, z також буде відкрито.
гумав

36
Зрозумів! Це має більше сенсу зараз. Ви можете додати це пояснення у свою відповідь. Це більш зрозуміло, ніж цитується документація.
Сурагч

378

Відповідь на це питання буде продемонструвати різницю між implementation, apiі compileна проекті.


Скажімо, у мене є проект з трьома модулями Gradle:

  • додаток (додаток для Android)
  • myandroidlibrary (бібліотека Android)
  • myjavalibrary (бібліотека Java)

appмає myandroidlibraryяк залежності. myandroidlibraryмає myjavalibrary як залежності.

Залежність1

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')      
}

Залежність2

Як ви думаєте, myandroidlibraryяк має виглядати build.gradle? Яку сферу ми повинні використовувати?

У нас є три варіанти:

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

Залежність3

Яка різниця між ними і чим я повинен користуватися?

Компілювати або Api (варіант №2 або №3) Залежність4

Якщо ви використовуєте 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)

Залежність5

Якщо ви використовуєте 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 Library - конфігурації, які використовуються для оголошення залежностей для більш детального пояснення.

Зразок проекту для цієї відповіді доступний на https://github.com/aldoKelvianto/ImplementationVsCompile


1
У мене є додавання залежності до одного файлу jar за допомогою реалізації, якщо він не відкриває доступ до нього, чому я все ще можу отримати, і мій код працює нормально?
smkrn110

@ smkrn110 реалізація відкриє вашу бібліотеку jar, але не вашу залежність бібліотек.
альдок

2
@WijaySharma прийнята відповідь зазначає, що compileне гарантує те саме, що apiгарантує.
Sub 6 Ресурси

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 бібліотеки, тоді як конфігурація реалізації повинна використовуватися для декларування залежностей, які є внутрішніми для компонента.

Для подальшого пояснення див. Це зображення. Коротке пояснення


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
Чисте і стисле пояснення. Дякую!
LeOn - Хан Лі

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

Не відповідає безпосередньо на питання
skryvets

1
Існує також developmentOnly
Hohenheimsenberg

Що я повинен використовувати, якщо мені потрібен час виконання та компіляція? В даний час я implementationслідував за a runtime.
Maroun

8

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

  • Якщо ви працюєте над інтерфейсом або модулем, який забезпечує підтримку інших модулів, виявляючи членів заявленої залежності, ви повинні використовувати "api".
  • Якщо ви робите додаток або модуль, який збирається впроваджувати або використовувати заявлену залежність внутрішньо, використовуйте "реалізація".
  • 'compile' працює так само, як 'api', однак, якщо ви лише впроваджуєте або використовуєте будь-яку бібліотеку, 'імплементація' буде працювати краще і економить ваші ресурси.

прочитайте відповідь @aldok для вичерпного прикладу.


Але річ у тому, що якщо людина свідомо приїхала сюди, шукаючи відповідь на ці питання, то він все-таки не є мирянин.
Ришав

6

Оскільки версія 5.6.3 Документація Gradle надає прості правила визначення, чи compileслід замінити стару залежність (або нову) implementationна apiзалежність або залежність:

  • Віддайте перевагу implementationконфігурації, apiколи це можливо

Це не дозволяє залежним від компіляційного класу споживача. Крім того, споживачі негайно не зможуть компілювати, якщо якісь типи впровадження випадково просочаться у загальнодоступний API.

Тож коли слід використовувати apiконфігурацію? Залежність API - це така, яка містить принаймні один тип, який піддається впливу бінарного інтерфейсу бібліотеки, який часто називають його ABI (Application Binary Interface). Це включає, але не обмежується ними:

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

Навпаки, будь-який тип, який використовується у наведеному нижче списку, не має відношення до ABI, а тому повинен бути оголошений як implementationзалежність:

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

6

Gradle 3.0 введено наступні зміни:

  • compile -> api

    api Ключове слово те саме, що застаріле compile

  • compile -> implementation

    Є кращим способом, оскільки має деякі переваги. implementationвиставити залежність лише на один рівень вгору під час збирання (залежність доступна під час виконання). В результаті у вас швидше нарощувати (не потрібно перекомпілювати споживачів, які вищі на 1 рівень вище)

  • provided -> compileOnly

    Ця залежність доступна лише під час компіляції (залежність недоступна під час виконання). Ця залежність не може бути перехідною і бути .aar. Його можна використовувати з процесором анотації часу компіляції та дозволяє зменшити кінцевий вихідний файл

  • compile -> annotationProcessor

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

  • apk -> runtimeOnly

    Залежність недоступна в час компіляції, але доступна під час виконання.


Отже, іншими словами, api = public, implementation = internalі compileOnly = private- мені потрібно створити такі псевдоніми для цих функцій , оскільки вони дуже заплутаним.
t3chb0t
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.