Реалізація Gradle vs API


231

Я намагаюся зрозуміти це , в чому різниця між apiі implementationконфігурацією при побудові своїх залежностей.
У документації йдеться про те, що implementationкращий час на побудову, але, побачивши цей коментар у подібному питанні, я повинен задуматися, чи правда це.
Оскільки я не фахівець з граду, я сподіваюся, що хтось може допомогти. Я вже читав документацію, але мені цікаво було легко зрозуміти пояснення.


1
Ви тут читали ?
MatPag

як насправді, я це зробив, але, як я вже сказав, цей коментар здивував це. тож я зараз свого роду програв
reinaldomoreira

Ви, ймовірно, переключите залежність своїх бібліотек з compileна api. Бібліотеки, якими ви користуєтесь внутрішньо, можуть використовувати деякі приватні реалізації, які не піддаються впливу остаточної бібліотеки, щоб вони були прозорими для вас. Ці "внутрішньо-приватні" залежності можна переключити, implementationі коли плагін Android gradle складе ваше додаток, він пропустить компіляцію цих залежностей, що призведе до меншого часу збірки (але ці залежності будуть доступні під час виконання). Очевидно, що ви можете зробити те ж саме, якщо у вас є локальні бібліотеки модулів
MatPag

1
Ось коротке графічне пояснення "api" та "реалізації": jeroenmols.com/blog/2017/06/14/androidstudio3
albert c braun

1
це дивовижний пост! дякую @albertbraun
reinaldomoreira

Відповіді:


418

compileКлючове слово Gradle застаріле на користь apiта implementationключових слів для налаштування залежностей.

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

Для розуміння implementationключового слова розглянемо наступний приклад.

ПРИКЛАД

Припустимо, у вас є бібліотека під назвою, MyLibraryяка внутрішньо використовує іншу бібліотеку під назвою InternalLibrary. Щось на зразок цього:

    // 'InternalLibrary' module
    public class InternalLibrary {
        public static String giveMeAString(){
            return "hello";
        }
    }
    // 'MyLibrary' module
    public class MyLibrary {
        public String myString(){
            return InternalLibrary.giveMeAString();
        }
    }

Припустимо, конфігурація MyLibrary build.gradleвикористання виглядає так:apidependencies{}

dependencies {
    api project(':InternalLibrary')
}

Ви хочете використовувати MyLibraryу своєму коді, щоб у додатку build.gradleдодавати цю залежність:

dependencies {
    implementation project(':MyLibrary')
}

За допомогою apiконфігурації (або застарілої compile) ви можете отримати доступ InternalLibraryу коді програми:

// Access 'MyLibrary' (granted)
MyLibrary myLib = new MyLibrary();
System.out.println(myLib.myString());

// Can ALSO access the internal library too (and you shouldn't)
System.out.println(InternalLibrary.giveMeAString());

Таким чином модуль MyLibraryпотенційно "просочує" внутрішню реалізацію чогось. Ви не повинні (бути в змозі) використовувати це, оскільки це не безпосередньо імпортоване вами.

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

dependencies {
    implementation project(':InternalLibrary')
}

ви більше не зможете зателефонувати InternalLibrary.giveMeAString()у свій додаток.

Така стратегія боксу дозволяє плагіну Android Gradle знати, що якщо ви щось редагуєте InternalLibrary, він повинен викликати лише перекомпіляцію, MyLibraryа не перекомпіляцію всього вашого додатка, оскільки ви не маєте доступу до нього InternalLibrary.

Коли у вас багато вкладених залежностей, цей механізм може значно прискорити збірку. (Дивіться відео, пов'язане в кінці, для повного розуміння цього)

ВИСНОВКИ

  • При переході на новий Android Gradle плагін 3.xx, ви повинні замінити всі ваші compileз implementationключовим словом (1 *) . Потім спробуйте скласти і протестувати додаток. Якщо все нормально, залиште код таким, як є, якщо у вас є проблеми, ви, мабуть, щось не так з вашими залежностями або ви використовували щось, що зараз є приватним і не доступнішим. Пропозиція інженера плагінів Android Gradle Джерома Дочеса (1 ) * )

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

Корисна стаття, що показує різницю між впровадженням та api

ДОВІДКИ (Це те саме відео, розбите для економії часу)

Google I / O 2017 - Як прискорити створення Gradle (ПОВНЕ ВІДЕО)

Google I / O 2017 - Як прискорити побудову Gradle (НОВОЇ ДІЯЛЬНОЇ ГРАНІ ПЛУГІН 3.0.0)

Google I / O 2017 - Як прискорити створення Gradle (посилання на 1 * )

Документація на Android


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

1
Це добре і працює на налагодженнях, але при використанні ProGuard (у версіях релізів) MyLibrary#myString()вийде з ладу, оскільки ProGuard буде InternalLibraryвидалений. Яка найкраща практика для android-libs для використання в програмах ProGuard'ed?
hardysim

3
Я думаю, що відповідь не є точною, програма може використовувати будь-яку область, яку вона хоче для MyLibrary. Він буде бачити чи ні Внутрішню бібліотеку залежно від того, використовує чи ні MyLibrary api / application.
Snicolas

2
дякую людині. дивовижне пояснення, набагато краще, ніж те, яке наведено в офіційних документах Android
Генрі

2
це прекрасне пояснення. Теорія та бетон блискуче змішалися. Молодці. Дякую за це
Пітер Кан

134

Мені подобається думати про apiзалежність як публічну (бачиться іншими модулями), а implementationзалежність - як приватну (бачиться лише цим модулем).

Зауважте, що на відміну від public/ privateзмінних та методів, api/ implementationзалежності не виконуються під час виконання. Це лише оптимізація часу нарощування, яка дозволяє Gradleзнати, які модулі потрібно перекомпілювати, коли одна із залежностей змінює свій API.


16
Любіть простоту цієї відповіді дуже дякую
Кевін

2
Справжня відмінність (AFAICT) полягає в тому, що генерований файл pom розміщує apiзалежності в області "компілювати" (вони будуть включені як залежності у вашій бібліотеці і все, що залежить від вашої бібліотеки) та implementationзалежності в області "час виконання" (вони краще бути в classpath, коли працює ваш код, але вони не потрібні для компіляції іншого коду, який використовує вашу бібліотеку).
Shadow Man

@ShadowMan Це детальна інформація про реалізацію плагіна, що відповідає за генерування файлу POM, про те, як він відображає області Gradle на області Maven .
dev.bmax

1
Ви повинні використовувати implementationдля будь-якої залежності, яка необхідна для запуску (і для збирання вашої бібліотеки), але це не повинно автоматично втягуватися в проекти, які використовують вашу бібліотеку. Прикладом може бути jax-rs, ваша бібліотека може використовувати RESTeasy, але вона не повинна втягувати ці вкладки в жоден проект, який використовує вашу бібліотеку, оскільки вони, можливо, захочуть використовувати Джерсі.
Shadow Man

1
ось як ви знаєте, хтось отримує свої речі: D дякую за просту і чітку відповідь
Elias

12

Подумайте, у вас є appмодуль, який використовується lib1як бібліотека і lib1використовується lib2як бібліотека. Що - щось на зразок цього: app -> lib1 -> lib2.

Тепер при використанні api lib2в lib1, тоді ви app можете бачити lib2 коди при використанні: api lib1або implementation lib1в appмодулі.

АЛЕ при використанні implementation lib2в lib1, то app не можете побачити на lib2коди.


5

Відповіді @matpag та @ dev- bmax досить чіткі, щоб люди зрозуміли різні звички між впровадженням та api. Я просто хочу зробити додаткове пояснення з іншого боку, сподіваюся допомогти людям, які мають те саме питання.

Я створив два проекти для тестування:

  • проект A як проект бібліотеки Java під назвою 'frameworks-web-gradle-plugin' залежить від 'org.springframework.boot: spring-boot-gradle-plugin: 1.5.20. RELEASE'
  • проект B залежить від проекту A від реалізації 'com.example.frameworks.gradle: frameworks-web-gradle-plugin: 0.0.1-SNAPSHOT'

Описана вище ієрархія залежностей виглядає так:

[project-b] -> [project-a] -> [spring-boot-gradle-plugin]

Тоді я перевірив такі сценарії:

  1. Зробити проект A залежить від 'org.springframework.boot: spring-boot-gradle-plugin: 1.5.20. RELEASE' за реалізацією .

    Запустити gradle dependenciesкоманду в терміналі в кореневому dir ject B , з наступним скріншотом виводу, ми можемо побачити, що 'spring-boot-gradle-plugin' з'являється в дереві залежностей runtimeClasspath, але не в compileClasspath's, я думаю, що саме тому ми не можемо зробити використання бібліотеки, декларованої за допомогою реалізації, просто не буде компіляцією.

    введіть тут опис зображення

  2. Зробити проект A залежить від 'org.springframework.boot: spring-boot-gradle-plugin: 1.5.20.RELEASE' від api

    gradle dependenciesЗнову запустіть команду в терміналі в кореневому реєстрі B об'єкта B. Тепер 'spring-boot-gradle-plugin' з'являється у дереві залежностей compileClasspath та runtimeClasspath.

    введіть тут опис зображення

Суттєва відмінність, яку я помітив, полягає в тому, що залежність у проекті виробника / бібліотечного проекту, оголошеного способом впровадження, не з’явиться в compileClasspath споживчих проектів, так що ми не можемо використовувати відповідну лібу у споживчих проектах.


2

З документації gradle :

Давайте розглянемо дуже простий сценарій збірки для проекту на базі JVM.

plugins {
    id 'java-library'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.hibernate:hibernate-core:3.6.7.Final'
    api 'com.google.guava:guava:23.0'
    testImplementation 'junit:junit:4.+'
}

реалізація

Залежності, необхідні для складання джерела виробництва проекту, які не є частиною API, що піддається проекту. Наприклад, проект використовує Hibernate для своєї внутрішньої стійкості шару.

апі

Залежності, необхідні для складання виробничого джерела проекту, що є частиною API, що піддається проекту. Наприклад, проект використовує Guava та розкриває публічні інтерфейси з класами Guava в їхніх підписах методів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.