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

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

Ответ 1

Ключевое слово 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.X.X, вы должны заменить все свои compile на ключевое слово implementation (1 *). Затем попробуйте скомпилировать и протестировать ваше приложение. Если все в порядке, оставьте код как есть, если у вас есть проблемы, возможно, у вас что-то не так с вашими зависимостями, или вы использовали что-то частное и не более доступное. Предложение от инженера плагинов Gradle для Android Джерома Дочеза (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 документация

Ответ 2

Мне нравится думать о зависимости api как public (видятся другими модулями), тогда как implementation зависимость как private (видна только этим модулем).

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

Ответ 3

lib1 вас есть модуль app который использует lib1 в качестве библиотеки, а lib1 использует lib2 в качестве библиотеки. Примерно так: app → lib1 → lib2.

Теперь при использовании api lib2 в lib1 app может видеть коды lib2 при использовании: api lib1 или implementation lib1 в модуле app.

НО при использовании implementation lib2 в lib1, app не может видеть коды lib2.

Ответ 4

При использовании Android Gradle плагина 3.0 в вашем проекте вы могли заметить, что ключевое слово компиляции теперь устарело в пользу реализации и api. Давайте рассмотрим их оба примера.

Предположим, проект с четырьмя библиотечными модулями.

  • LibraryA
  • библиотека B
  • LibraryC
  • LibraryD

Для чего дерево зависимостей выглядит следующим образом:

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

Библиотека D

class ClassD {

    fun tellMeAJoke():String{
        return "You are funny :D"
    }
}

Библиотека C

class ClassC {
    fun tellMeAJoke(): String {
        return "You are funny :C"
    }
}

Библиотека B

class ClassB {

    val b = ClassD()

    fun whereIsMyJoke(): String {
        return b.tellMeAJoke()
    }
}

Библиотека A

class ClassA {

    val c = ClassC()

    fun whereIsMyJoke(): String {
        return c.tellMeAJoke()
    }
}

Из вышеприведенных файлов классов видно, что LibraryA и LibraryB в зависимости от LibraryC и LibraryD соответственно. Поэтому эти зависимости, необходимо добавить в build.gradle файлы. Скомпилируйте (2.0) или Api (3.0):

Новое ключевое слово api точно совпадает с старым ключевым словом компиляции. Таким образом, если весь компилятор заменен на api, он работает отлично. Теперь давайте добавим зависимость LibraryD с помощью ключевого слова api в LibraryB.

dependencies {
          . . . . 
          api project(path: ':libraryD')
}

Аналогично, LibraryB добавляется в модуль приложения.

dependencies {
          . . . . 
          api project(path: ':libraryB')
}

Теперь доступ к библиотекам и библиотекам можно получить из модуля приложения. В примерном приложении обе библиотеки обращаются следующим образом:

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

Реализация (3.0):

Время, чтобы узнать, как реализация отличается от api. Вернемся к примеру, на этот раз можно импортировать LibraryC в библиотеку с использованием ключевого слова реализации.

dependencies {
          . . . . 
          implementation project(path: ':libraryC')
}

То же самое для модуля App.

dependencies {
          . . . . 
          implementation project(path: ':libraryA')
}

Теперь, если мы попытаемся получить доступ к LibraryC из модуля приложения, студия Android выведет ошибку.

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

Это означает, что доступ к LibraryC не может быть получен непосредственно из модуля App, если мы используем реализацию вместо api. Итак, в чем польза от этого?

Реализация vs api:

В первом сценарии LibraryD скомпилирован с помощью api. Если какое-либо изменение реализовано внутри LibraryD, Gradle необходимо перекомпилировать библиотечные библиотеки, библиотечные библиотеки и все другие модули, которые импортируют LibraryB, поскольку любой другой модуль может использовать реализацию LibraryD.

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

Но во втором случае, если какая-либо реализация в LibraryC изменилась, Gradle просто нужно перекомпилировать LibraryC и LibraryA, поскольку любой другой класс, который не импортирует LibraryC напрямую, не может использовать какую-либо его реализацию.

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

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

Полная сборка:

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

Изменение в библиотеке D:

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

Изменение в библиотеке C:

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

Ответ 5

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