Реализация Gradle против конфигурации API

232

Я пытаюсь выяснить, в чем разница apiи implementationконфигурация при построении моих зависимостей.
В документации говорится, что implementationвремя сборки лучше, но, увидев этот комментарий в похожем вопросе, я удивился, правда ли это.
Так как я не специалист в Gradle, я надеюсь, что кто-то может помочь. Я уже прочитал документацию, но мне было интересно найти простое для понимания объяснение.

reinaldomoreira
источник
1
Вы читали здесь ?
MatPag
как на самом деле, я и сделал, но, как я уже сказал, этот комментарий заставил задуматься об этом. так что я вроде как заблудился
reinaldomoreira
Вы, вероятно, переключите свои библиотечные зависимости с compileна api. Библиотеки, которые вы используете внутри, могут использовать некоторые частные реализации, которые не представлены в конечной библиотеке, поэтому они прозрачны для вас. К этим «внутренним-частным» зависимостям можно переключиться, implementationи когда плагин Android gradle скомпилирует ваше приложение, он пропустит компиляцию этих зависимостей, что приведет к меньшему времени сборки (но эти зависимости будут доступны во время выполнения). Очевидно, вы можете сделать то же самое, если у вас есть локальные библиотеки модулей
MatPag
1
Вот краткое графическое объяснение «api» и «реализации»: jeroenmols.com/blog/2017/06/14/androidstudio3
Альберт
1
это потрясающий пост! спасибо @albertbraun
reinaldomoreira

Ответы:

419

Gradle compileключевого слово осуждалось в пользу 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использует apiконфигурацию dependencies{}следующим образом:

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 Джером Dochez (1 ) * )

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

Полезная статья, демонстрирующая разницу между реализацией и API

ССЫЛКИ (Это то же видео, разделенное для экономии времени)

Google I / O 2017 - Как ускорить сборку Gradle (ПОЛНОЕ ВИДЕО)

Google I / O 2017 - Как ускорить сборку Gradle (ТОЛЬКО ДЛЯ НОВОЙ GRADLE PLUGIN 3.0.0)

Google I / O 2017 - Как ускорить сборку Gradle (ссылка на 1 * )

Android документация

MatPag
источник
4
Я заметил, что api не очень хорошо работает в библиотечных модулях. Если я использую его, я все равно не могу получить доступ к зависимостям из моего проекта приложения. Я могу получить доступ только к коду в этой библиотеке.
Аллан В.
1
Это нормально и работает на сборках отладки, но при использовании ProGuard (в версиях выпуска) происходит MyLibrary#myString()сбой, потому что ProGuard будет InternalLibraryудален. Какова лучшая практика для Android-библиотек, которые будут использоваться в приложениях ProGuard?
Hardysim
3
Я думаю, что ответ не точный, приложение может использовать любой объем, который он хочет для MyLibrary. Он будет видеть или нет InternalLibrary в зависимости от того, использует ли MyLibrary API / реализации.
Сниколас
2
Спасибо чувак. потрясающее объяснение, намного лучше, чем приведенное в официальных документах Android
Генри
2
это прекрасное объяснение. Теория и бетон смешались блестяще. Отлично сработано. Спасибо за это
Питер Кан
134

Мне нравится думать о apiзависимости как общедоступной (видимой другими модулями), а implementationзависимость как частной (видимой только этим модулем).

Обратите внимание, что в отличие от public/ privateпеременных и методов api/ implementationзависимости не применяются во время выполнения. Это просто оптимизация во время сборки, которая позволяет Gradleузнать, какие модули нужно перекомпилировать, когда одна из зависимостей меняет свой API.

dev.bmax
источник
16
Люблю простоту этого ответа, большое спасибо
Кевин Жиль
2
Реальная разница (AFAICT) заключается в том, что сгенерированный pom-файл помещает apiзависимости в область «компиляции» (они будут включены в качестве зависимостей в вашу библиотеку и все, что зависит от вашей библиотеки) и implementationзависимости в область «времени выполнения» (они лучше должны быть в classpath, когда ваш код работает, но они не нужны для компиляции другого кода, который использует вашу библиотеку).
Человек-тень
@ShadowMan Это деталь реализации плагина, отвечающего за генерацию POM-файла, как он сопоставляет области Gradle с областями Maven .
dev.bmax
1
Вы должны использовать implementationлюбую зависимость, которая требуется для запуска (и для вашей библиотеки для компиляции), но она не должна автоматически включаться в проекты, которые используют вашу библиотеку. Примером может служить jax-rs, ваша библиотека может использовать RESTeasy, но она не должна втягивать эти библиотеки в любой проект, который использует вашу библиотеку, так как они могут захотеть использовать вместо этого Jersey.
Человек-тень
1
вот как вы знаете, кто-то получит свои вещи: D спасибо за простой и понятный ответ
Элиас
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' by реализацией .

    Запустите gradle dependenciesкоманду в терминале в корневой директории объекта B , со следующим снимком экрана: мы видим, что «spring-boot-gradle-plugin» появляется в дереве зависимостей runtimeClasspath, но не в compileClasspath, я думаю, именно поэтому мы не можем сделать использование библиотеки, которая объявляет использование реализации, она просто не будет компилироваться.

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

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

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

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

Существенное отличие, которое я заметил, заключается в том, что зависимость в проекте «производитель / библиотека», объявленная способом реализации, не появится в compileClasspath потребительских проектов, поэтому мы не можем использовать соответствующую библиотеку в потребительских проектах.

Rong.l
источник
2

Из учебной документации :

Давайте посмотрим на очень простой скрипт сборки для проекта на основе 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 в сигнатурах их методов.

Камило Силва
источник