Gradle-Implementierung vs. API-Konfiguration


231

Ich versuche herauszufinden, was der Unterschied zwischen apiund implementationKonfiguration ist, während ich meine Abhängigkeiten aufbaue.
In der Dokumentation heißt es, dass implementationdie Bauzeit besser ist, aber als ich diesen Kommentar in einer ähnlichen Frage sah, musste ich mich fragen, ob er wahr ist.
Da ich kein Gradle-Experte bin, hoffe ich, dass jemand helfen kann. Ich habe die Dokumentation bereits gelesen , habe mich aber über eine leicht verständliche Erklärung gewundert.


1
Haben Sie lesen hier ?
MatPag

Eigentlich habe ich es getan, aber wie gesagt, dieser Kommentar hat mich darüber gewundert.
Also

Sie werden wahrscheinlich Ihre Bibliotheksabhängigkeiten von compilenach wechseln api. Die Bibliotheken, die Sie intern verwenden, können einige private Implementierungen verwenden, die in der endgültigen Bibliothek nicht verfügbar sind, sodass sie für Sie transparent sind. Diese "intern-privaten" Abhängigkeiten können umgeschaltet werden. implementationWenn das Android Gradle-Plugin Ihre App kompiliert, wird die Kompilierung dieser Abhängigkeiten übersprungen, was zu einer kürzeren Erstellungszeit führt (diese Abhängigkeiten sind jedoch zur Laufzeit verfügbar).
Natürlich

1
Hier ist eine kurze grafische Erklärung von 'API' und 'Implementierung': jeroenmols.com/blog/2017/06/14/androidstudio3
albert c braun

1
Das ist ein großartiger Beitrag! danke @albertbraun
reinaldomoreira

Antworten:


417

Das Gradle- compileSchlüsselwort wurde zugunsten der Schlüsselwörter apiund veraltet implementation, um Abhängigkeiten zu konfigurieren.

Die Verwendung apientspricht der Verwendung der veralteten compile. Wenn Sie also alle compiledurch apialles ersetzen , funktioniert dies wie immer.

Um das implementationSchlüsselwort zu verstehen, betrachten Sie das folgende Beispiel.

BEISPIEL

Angenommen, Sie haben eine Bibliothek mit dem Namen MyLibrary, die intern eine andere Bibliothek mit dem Namen verwendet InternalLibrary. Etwas wie das:

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

Angenommen, die MyLibrary build.gradleVerwendungskonfiguration apisieht folgendermaßen aus dependencies{}:

dependencies {
    api project(':InternalLibrary')
}

Sie möchten MyLibraryin Ihrem Code verwenden, also build.gradlefügen Sie in Ihren Apps diese Abhängigkeit hinzu:

dependencies {
    implementation project(':MyLibrary')
}

Mit der apiKonfiguration (oder veraltet compile) können Sie InternalLibraryin Ihrem Anwendungscode darauf zugreifen :

// 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());

Auf diese Weise MyLibrary"verliert" das Modul möglicherweise die interne Implementierung von etwas. Sie sollten das nicht verwenden können, da es nicht direkt von Ihnen importiert wird.

Die implementationKonfiguration wurde eingeführt, um dies zu verhindern. Also jetzt, wenn Sie implementationanstelle von apiin verwenden MyLibrary:

dependencies {
    implementation project(':InternalLibrary')
}

Sie können InternalLibrary.giveMeAString()Ihren App-Code nicht mehr aufrufen .

Durch diese Art der Boxstrategie kann das Android Gradle-Plugin erkennen, dass beim Bearbeiten InternalLibrarynur die Neukompilierung MyLibraryund nicht die Neukompilierung Ihrer gesamten App ausgelöst werden muss , da Sie keinen Zugriff darauf haben InternalLibrary.

Wenn Sie viele verschachtelte Abhängigkeiten haben, kann dieser Mechanismus den Build erheblich beschleunigen. (Sehen Sie sich das am Ende verlinkte Video an, um dies vollständig zu verstehen.)

SCHLUSSFOLGERUNGEN

  • Wenn Sie zum neuen Android Gradle-Plugin 3.XX wechseln, sollten Sie alle Ihre compiledurch das implementationSchlüsselwort (1 *) ersetzen . Versuchen Sie dann, Ihre App zu kompilieren und zu testen. Wenn alles in Ordnung ist, lassen Sie den Code unverändert. Wenn Sie Probleme haben, haben Sie wahrscheinlich einen Fehler mit Ihren Abhängigkeiten oder Sie haben etwas verwendet, das jetzt privat und nicht zugänglicher ist. Vorschlag von Android Gradle Plugin Ingenieur Jerome Dochez (1 ) * )

  • Wenn Sie ein Bibliotheks-Mantainer sind, sollten Sie diese apifür jede Abhängigkeit verwenden, die für die öffentliche API Ihrer Bibliothek benötigt wird, während Sie sie implementationfür Testabhängigkeiten oder Abhängigkeiten verwenden, die von den Endbenutzern nicht verwendet werden dürfen.

Nützlicher Artikel Den Unterschied zwischen Implementierung und API aufzeigen

REFERENZEN (Dies ist das gleiche Video, das aus Zeitgründen aufgeteilt wurde.)

Google I / O 2017 - Wie schnell Gradle erstellt wird (FULL VIDEO)

Google I / O 2017 - Wie schnell Gradle erstellt wird (NUR NEUES GRADLE PLUGIN 3.0.0-TEIL)

Google I / O 2017 - Beschleunigung der Erstellung von Gradle (Verweis auf 1 * )

Android-Dokumentation


4
Mir ist aufgefallen, dass API in Bibliotheksmodulen nicht gut zu funktionieren scheint. Wenn ich es verwende, kann ich immer noch nicht auf die Abhängigkeiten aus meinem App-Projekt zugreifen. Ich kann nur auf den Code in dieser Bibliothek selbst zugreifen.
Allan W

1
Dies ist in Ordnung und funktioniert bei Debug-Builds, aber bei Verwendung von ProGuard (bei Release-Versionen) MyLibrary#myString()stürzt es ab, da ProGuard InternalLibraryentfernt wurde. Was ist die beste Vorgehensweise für Android-Bibliotheken zur Verwendung in ProGuard-Apps?
Hardysim

3
Ich denke, die Antwort ist nicht korrekt, die Anwendung kann jeden gewünschten Bereich für die MyLibrary verwenden. Die InternalLibrary wird angezeigt oder nicht, je nachdem, ob die MyLibrary API / Implementierung verwendet oder nicht.
Snicolas

2
Danke, Mann. tolle Erklärung, viel besser als die in den offiziellen Dokumenten von Android
Henry

2
Das ist eine schöne Erklärung. Theorie und Beton vermischten sich hervorragend. Gut gemacht. Danke dafür
Peter Kahn

133

Ich denke gerne an eine apiAbhängigkeit als öffentlich (von anderen Modulen gesehen) und eine implementationAbhängigkeit als privat (nur von diesem Modul gesehen).

Beachten Sie, dass im Gegensatz zu public/ privateVariablen und Methoden api/ implementationAbhängigkeiten von der Laufzeit nicht erzwungen werden. Dies ist lediglich eine Optimierung der Erstellungszeit, mit der ermittelt werden kann Gradle, welche Module neu kompiliert werden müssen, wenn eine der Abhängigkeiten ihre API ändert.


15
Ich liebe die Einfachheit dieser Antwort. Vielen Dank
Kevin Gilles

2
Der wirkliche Unterschied (AFAICT) besteht darin, dass die generierte POM-Datei apiAbhängigkeiten in den Bereich "Kompilieren" (sie werden als Abhängigkeiten in Ihre Bibliothek aufgenommen und alles, was von Ihrer Bibliothek abhängt) und implementationAbhängigkeiten in den Bereich "Laufzeit" (sie sind besser in den Bereich " Klassenpfad, wenn Ihr Code ausgeführt wird, aber nicht benötigt wird, um anderen Code zu kompilieren, der Ihre Bibliothek verwendet.
Shadow Man

@ShadowMan Es ist ein Implementierungsdetail des Plugins, das für die Generierung der POM-Datei verantwortlich ist und wie Gradle- Bereiche Maven- Bereichen zugeordnet werden.
dev.bmax

1
Sie sollten implementationfür alle Abhängigkeiten verwenden, die zum Ausführen erforderlich sind (und für das Kompilieren Ihrer Bibliothek), aber nicht automatisch in Projekte gezogen werden sollten, die Ihre Bibliothek verwenden. Ein Beispiel wäre jax-rs. Ihre Bibliothek verwendet möglicherweise RESTeasy, sollte diese Bibliotheken jedoch nicht in ein Projekt ziehen, das Ihre Bibliothek verwendet, da sie möglicherweise stattdessen Jersey verwenden möchten.
Shadow Man

1
So weißt du, dass jemand seine Sachen bekommt: D Danke für die einfache und klare Antwort
Elias Fazel

12

Betrachten Sie appModul , das verwendet lib1als Bibliothek und lib1Verwendungen lib2als Bibliothek. So etwas wie das : app -> lib1 -> lib2.

Jetzt bei der Verwendung von api lib2in lib1, dann app sehen lib2 Codes bei der Verwendung von : api lib1oder implementation lib1im appModul.

ABER bei Verwendung implementation lib2in lib1kann dann die Codes app nicht sehenlib2 .


5

Die Antworten von @matpag und @ dev-bmax sind klar genug, damit die Benutzer unterschiedliche Verwendungen zwischen Implementierung und API verstehen. Ich möchte nur eine zusätzliche Erklärung aus einem anderen Blickwinkel geben und hoffe, Menschen zu helfen, die die gleiche Frage haben.

Ich habe zwei Projekte zum Testen erstellt:

  • Projekt A als Java-Bibliotheksprojekt mit dem Namen "Frameworks-Web-Gradle-Plugin" hängt von "org.springframework.boot: spring-boot-gradle-plugin: 1.5.20.RELEASE" ab.
  • Projekt B hängt von Projekt A durch die Implementierung 'com.example.frameworks.gradle: Frameworks-Web-Gradle-Plugin: 0.0.1-SNAPSHOT' ab.

Die oben beschriebene Abhängigkeitshierarchie sieht folgendermaßen aus:

[Projekt-b] -> [Projekt-a] -> [Spring-Boot-Gradle-Plugin]

Dann habe ich folgende Szenarien getestet:

  1. Das Erstellen von Projekt A hängt von 'org.springframework.boot: spring-boot-gradle-plugin: 1.5.20.RELEASE' durch Implementierung ab .

    Führen Sie den gradle dependenciesBefehl in einem Terminal im Stammverzeichnis von Objekt B aus. Mit dem folgenden Screenshot der Ausgabe können wir sehen, dass 'spring-boot-gradle-plugin' im Abhängigkeitsbaum von runtimeClasspath angezeigt wird, aber nicht in compileClasspath Die Verwendung einer Bibliothek, die mithilfe der Implementierung deklariert wurde, erfolgt nur nicht durch Kompilierung.

    Geben Sie hier die Bildbeschreibung ein

  2. Das Erstellen von Projekt A hängt von 'org.springframework.boot: spring-boot-gradle-plugin: 1.5.20.RELEASE' von api ab

    Führen Sie den gradle dependenciesBefehl in einem Terminal im Stammverzeichnis von Objekt B erneut aus. Jetzt wird 'spring-boot-gradle-plugin' sowohl im compileClasspath- als auch im runtimeClasspath-Abhängigkeitsbaum angezeigt.

    Geben Sie hier die Bildbeschreibung ein

Ein wesentlicher Unterschied, den ich bemerkt habe, ist, dass die Abhängigkeit im Produzenten- / Bibliotheksprojekt, die auf Implementierungsart deklariert wurde, nicht in compileClasspath von Verbraucherprojekten angezeigt wird, so dass wir die entsprechende Bibliothek in den Verbraucherprojekten nicht verwenden können.


2

Aus der Gradle-Dokumentation :

Schauen wir uns ein sehr einfaches Build-Skript für ein JVM-basiertes Projekt an.

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

Implementierung

Die zum Kompilieren der Produktionsquelle des Projekts erforderlichen Abhängigkeiten, die nicht Teil der vom Projekt bereitgestellten API sind. Beispielsweise verwendet das Projekt Hibernate für die Implementierung der internen Persistenzschicht.

api

Die zum Kompilieren der Produktionsquelle des Projekts erforderlichen Abhängigkeiten, die Teil der vom Projekt bereitgestellten API sind. Das Projekt verwendet beispielsweise Guava und macht öffentliche Schnittstellen mit Guava-Klassen in ihren Methodensignaturen verfügbar.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.