Was ist der Unterschied zwischen Implementierung und Kompilierung in Gradle?

1027

Nach dem Update auf Android Studio 3.0 und dem Erstellen eines neuen Projekts habe ich festgestellt, dass build.gradlees eine neue Möglichkeit gibt, neue Abhängigkeiten hinzuzufügen, anstatt compilees gibt implementationund statt testCompilegibt testImplementation.

Beispiel:

 implementation 'com.android.support:appcompat-v7:25.0.0'
 testImplementation 'junit:junit:4.12'

anstatt

 compile 'com.android.support:appcompat-v7:25.0.0'
 testCompile 'junit:junit:4.12'

Was ist der Unterschied zwischen ihnen und was soll ich verwenden?

humazed
quelle

Antworten:

1281

tl; dr

Einfach ersetzen:

  • compilemit implementation(wenn Sie keine Transitivität benötigen) oder api(wenn Sie Transitivität benötigen)
  • testCompile mit testImplementation
  • debugCompile mit debugImplementation
  • androidTestCompile mit androidTestImplementation
  • compileOnlyIst noch gültig. Es wurde in 3.0 hinzugefügt, um das bereitgestellte zu ersetzen und nicht zu kompilieren. ( providedEingeführt, als Gradle keinen Konfigurationsnamen für diesen Anwendungsfall hatte und ihn nach Mavens bereitgestelltem Bereich benannte.)

Dies ist eine der wichtigsten Änderungen, die mit Gradle 3.0 einhergehen und die Google auf der IO17 angekündigt hat .

Die compileKonfiguration ist jetzt veraltet und sollte durch implementationoder ersetzt werdenapi

Aus der Gradle-Dokumentation :

dependencies {
    api 'commons-httpclient:commons-httpclient:3.1'
    implementation 'org.apache.commons:commons-lang3:3.5'
}

Abhängigkeiten, die in den apiKonfigurationen angezeigt werden, werden den Verbrauchern der Bibliothek transitiv ausgesetzt und als solche im Kompilierungsklassenpfad der Verbraucher angezeigt.

Abhängigkeiten, die in der implementationKonfiguration gefunden werden, sind andererseits nicht für Verbraucher verfügbar und gelangen daher nicht in den Kompilierungsklassenpfad der Verbraucher. Dies hat mehrere Vorteile:

  • Abhängigkeiten gelangen nicht mehr in den Kompilierungsklassenpfad von Verbrauchern, sodass Sie nie versehentlich von einer transitiven Abhängigkeit abhängig sind
  • schnellere Kompilierung dank reduzierter Klassenpfadgröße
  • weniger Neukompilierungen, wenn sich Implementierungsabhängigkeiten ändern: Verbraucher müssten nicht neu kompiliert werden
  • Cleaner Publishing: In Verbindung mit dem neuen Maven-Publish-Plugin erstellen Java-Bibliotheken POM-Dateien, die genau unterscheiden, was zum Kompilieren mit der Bibliothek erforderlich ist und was zur Verwendung der Bibliothek zur Laufzeit erforderlich ist (mit anderen Worten, nicht mischen, was zum Kompilieren der Bibliothek selbst benötigt wird und was zum Kompilieren gegen die Bibliothek benötigt wird).

Die Kompilierungskonfiguration ist noch vorhanden, sollte jedoch nicht verwendet werden, da sie nicht die Garantien bietet, die die Konfigurationen apiund implementationbieten.


Hinweis: Wenn Sie nur eine Bibliothek in Ihrem App-Modul verwenden - im allgemeinen Fall - werden Sie keinen Unterschied bemerken.
Sie werden den Unterschied nur sehen, wenn Sie ein komplexes Projekt mit voneinander abhängigen Modulen haben oder eine Bibliothek erstellen.

humazed
quelle
137
Wer sind die "Verbraucher"?
Suragch
34
Der Verbraucher ist das Modul, das die Bibliothek verwendet. Im Fall von Android ist es die Android-Anwendung. Ich denke, das ist klar und ich bin mir nicht sicher, ob Sie danach fragen.
Humazed
21
So klang es auch für mich. Aber wenn ich eine Bibliothek erstelle, möchte ich natürlich, dass ihre API für die App verfügbar gemacht wird. Wie würde der App-Entwickler sonst meine Bibliothek verwenden? Deshalb verstehe ich die Bedeutung nicht, implementationdie Abhängigkeit zu verbergen. Ist meine Frage sinnvoll?
Suragch
235
Ja, es ist jetzt sinnvoll, wenn Ihre App von der Bibliothek x abhängt, die selbst von y, z abhängt. Wenn Sie implementationnur x api verwenden, wird belichtet, aber wenn Sie apiy verwenden, wird auch z belichtet.
Humazed
36
Ich habs! Das macht jetzt mehr Sinn. Sie können diese Erklärung in Ihre Antwort aufnehmen. Es ist klarer als die zitierte Dokumentation.
Suragch
380

Diese Antwort wird den Unterschied zwischen demonstrieren implementation, apiund compilean einem Projekt.


Angenommen, ich habe ein Projekt mit drei Gradle-Modulen:

  • App (eine Android-Anwendung)
  • myandroidlibrary (eine Android-Bibliothek)
  • myjavalibrary (eine Java-Bibliothek)

apphat myandroidlibraryals Abhängigkeiten. myandroidlibraryhat myjavalibrary als Abhängigkeiten.

Abhängigkeit1

myjavalibraryhat eine MySecretKlasse

public class MySecret {

    public static String getSecret() {
        return "Money";
    }
}

myandroidlibraryhat eine MyAndroidComponentKlasse, die den Wert der MySecretKlasse manipuliert .

public class MyAndroidComponent {

    private static String component = MySecret.getSecret();

    public static String getComponent() {
        return "My component: " + component;
    }    
}

Schließlich appinteressiert sich nur der Wert vonmyandroidlibrary

TextView tvHelloWorld = findViewById(R.id.tv_hello_world);
tvHelloWorld.setText(MyAndroidComponent.getComponent());

Lassen Sie uns nun über Abhängigkeiten sprechen ...

appmüssen verbrauchen :myandroidlibrary, also in appbuild.gradle verwenden implementation.

( Hinweis : Sie können auch api / compile verwenden. Halten Sie diesen Gedanken jedoch einen Moment lang fest.)

dependencies {
    implementation project(':myandroidlibrary')      
}

Abhängigkeit2

Wie myandroidlibrarysollte build.gradle Ihrer Meinung nach aussehen? Welchen Bereich sollten wir verwenden?

Wir haben drei Möglichkeiten:

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

Abhängigkeit3

Was ist der Unterschied zwischen ihnen und was soll ich verwenden?

Kompilieren oder API (Option 2 oder 3) Abhängigkeit4

Wenn Sie compileoder verwenden api. Unsere Android-Anwendung kann jetzt auf die myandroidcomponentAbhängigkeit zugreifen , die eine MySecretKlasse ist.

TextView textView = findViewById(R.id.text_view);
textView.setText(MyAndroidComponent.getComponent());
// You can access MySecret
textView.setText(MySecret.getSecret());

Implementierung (Option 1)

Abhängigkeit5

Wenn Sie die implementationKonfiguration verwenden, MySecretwird diese nicht angezeigt.

TextView textView = findViewById(R.id.text_view);
textView.setText(MyAndroidComponent.getComponent());
// You can NOT access MySecret
textView.setText(MySecret.getSecret()); // Won't even compile

Also, welche Konfiguration sollten Sie wählen? Das hängt wirklich von Ihrer Anforderung ab.

Wenn Sie Abhängigkeiten verfügbar machen möchten, verwenden Sie apioder compile.

Wenn Sie keine Abhängigkeiten verfügbar machen möchten (indem Sie Ihr internes Modul ausblenden), verwenden Sieimplementation .

Hinweis:

Dies ist nur ein Überblick über Gradle-Konfigurationen (siehe Tabelle 49.1). Java Library Plugin - Konfigurationen zum Deklarieren von Abhängigkeiten für detailliertere Erklärungen.

Das Beispielprojekt für diese Antwort ist unter https://github.com/aldoKelvianto/ImplementationVsCompile verfügbar

Aldok
quelle
1
Ich habe mithilfe der Implementierung eine Abhängigkeit zu einer JAR-Datei hinzugefügt. Wenn kein Zugriff darauf verfügbar ist, warum kann ich sie dann immer noch abrufen und mein Code funktioniert einwandfrei?
smkrn110
Die Implementierung von @ smkrn110 macht Ihre JAR-Bibliothek verfügbar, nicht jedoch Ihre JAR-Abhängigkeitsbibliotheken.
Aldok
2
@WijaySharma Die akzeptierte Antwort besagt, dass compilenicht die gleichen Dinge apigarantiert werden , die garantieren.
Sub 6 Ressourcen
9
Ich denke, das sollte die akzeptierte Antwort sein. Gut erklärt!
Shashank Kapsime
9
@ StevenW.Klassen, das ist die unverdienteste Ablehnung, von der ich je gehört habe. Wenn Sie der Meinung sind, dass die Reihenfolge der Informationen nicht optimal ist, schlagen Sie eine Bearbeitung vor, anstatt sich darüber zu beschweren
Tim
65

CompileKonfiguration war veraltet und sollte durch implementationoder ersetzt werden api.

Sie können die Dokumente unter https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_separation lesen .

Der kurze Teil ist:

Der Hauptunterschied zwischen dem Standard-Java-Plugin und dem Java Library-Plugin besteht darin, dass letzteres das Konzept einer API einführt, die Verbrauchern zur Verfügung steht. Eine Bibliothek ist eine Java-Komponente, die von anderen Komponenten verwendet werden soll. Dies ist ein sehr häufiger Anwendungsfall in Builds mit mehreren Projekten, aber auch, sobald Sie externe Abhängigkeiten haben.

Das Plugin stellt zwei Konfigurationen zur Verfügung, mit denen Abhängigkeiten deklariert werden können: API und Implementierung. Die API-Konfiguration sollte verwendet werden, um Abhängigkeiten zu deklarieren, die von der Bibliotheks-API exportiert werden, während die Implementierungskonfiguration verwendet werden sollte, um Abhängigkeiten zu deklarieren, die innerhalb der Komponente liegen.

Weitere Erläuterungen finden Sie in diesem Bild. Kurze Erklärung

Rishav
quelle
46

Kurze Lösung:

Der bessere Ansatz besteht darin, alle compileAbhängigkeiten durch implementationAbhängigkeiten zu ersetzen . Und nur dort, wo Sie die Schnittstelle eines Moduls verlieren, sollten Sie verwenden api. Das sollte viel weniger Neukompilierung verursachen.

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

Erkläre mehr:

Vor dem Android Gradle Plugin 3.0 : Wir hatten ein großes Problem: Eine Codeänderung führt dazu, dass alle Module neu kompiliert werden. Die Hauptursache dafür ist, dass Gradle nicht weiß, ob Sie die Schnittstelle eines Moduls durch ein anderes lecken oder nicht.

Nach Android Gradle Plugin 3.0 : Für das neueste Android Gradle Plugin müssen Sie jetzt explizit definieren, ob Sie die Schnittstelle eines Moduls verlieren. Auf dieser Grundlage kann es die richtige Wahl treffen, was neu kompiliert werden soll.

Daher wurde die compileAbhängigkeit verworfen und durch zwei neue ersetzt:

  • api: Sie lecken die Schnittstelle dieses Moduls über Ihre eigene Schnittstelle, was genau der alten compileAbhängigkeit entspricht

  • implementation: Sie verwenden dieses Modul nur intern und lecken es nicht über Ihre Schnittstelle

Jetzt können Sie Gradle explizit anweisen, ein Modul neu zu kompilieren, wenn sich die Schnittstelle eines verwendeten Moduls ändert oder nicht.

Mit freundlicher Genehmigung von Jeroen Mols Blog

Shayan Amani
quelle
2
Saubere und prägnante Erklärung. Vielen Dank!
LeOn - Han Li
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.  |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
Wajid Ali
quelle
Beantwortet die Frage nicht direkt
Skryvets
1
Es gibt auch eine Entwicklung
Hohenheimsenberg
Was soll ich verwenden, wenn ich sowohl Laufzeit als auch Kompilierungszeit benötige? Derzeit habe ich implementationvon einem gefolgt runtime.
Maroun
8

Der kurze Unterschied in der Amtszeit von Laien ist:

  • Wenn Sie an einer Schnittstelle oder einem Modul arbeiten, die / das andere Module unterstützt, indem Sie die Mitglieder der angegebenen Abhängigkeit verfügbar machen, sollten Sie 'api' verwenden.
  • Wenn Sie eine Anwendung oder ein Modul erstellen, die bzw. das die angegebene Abhängigkeit intern implementieren oder verwenden soll, verwenden Sie 'Implementierung'.
  • 'compile' funktionierte genauso wie 'api'. Wenn Sie jedoch nur eine Bibliothek implementieren oder verwenden, funktioniert 'implement' besser und spart Ressourcen.

Lesen Sie die Antwort von @aldok für ein umfassendes Beispiel.

Rushabh Agarwal
quelle
Aber die Sache ist, dass wenn jemand absichtlich hierher kam, um die Antwort auf diese Fragen zu suchen, er schließlich kein Laie ist.
Rishav
6

Seit Version 5.6.3 bietet die Gradle-Dokumentation einfache Faustregeln, um festzustellen, ob eine alte compile(oder eine neue) Abhängigkeit durch eine implementationoder eine apiAbhängigkeit ersetzt werden sollte:

  • Ziehen Sie die implementationKonfiguration nach apiMöglichkeit vor

Dadurch werden die Abhängigkeiten vom Kompilierungsklassenpfad des Verbrauchers ferngehalten. Darüber hinaus können die Verbraucher sofort nicht kompilieren, wenn versehentlich Implementierungstypen in die öffentliche API gelangen.

Wann sollten Sie die apiKonfiguration verwenden? Eine API-Abhängigkeit enthält mindestens einen Typ, der in der Binärschnittstelle der Bibliothek verfügbar gemacht wird und häufig als ABI (Application Binary Interface) bezeichnet wird. Dies beinhaltet, ist aber nicht beschränkt auf:

  • Typen, die in Superklassen oder Schnittstellen verwendet werden
  • Typen, die in öffentlichen Methodenparametern verwendet werden, einschließlich generischer Parametertypen (wobei public für Compiler sichtbar ist, dh öffentliche, geschützte und paketierte private Mitglieder in der Java-Welt).
  • Typen, die in öffentlichen Feldern verwendet werden
  • öffentliche Annotationstypen

Im Gegensatz dazu ist jeder Typ, der in der folgenden Liste verwendet wird, für den ABI irrelevant und sollte daher als deklariert werden implementation Abhängigkeit :

  • Typen, die ausschließlich in Methodenkörpern verwendet werden
  • Typen, die ausschließlich in privaten Mitgliedern verwendet werden
  • Typen, die ausschließlich in internen Klassen enthalten sind (in zukünftigen Versionen von Gradle können Sie deklarieren, welche Pakete zur öffentlichen API gehören).
Pom12
quelle
6

Gradle 3.0 nächste Änderungen eingeführt:

  • compile -> api

    api Schlüsselwort ist das gleiche wie veraltet compile

  • compile -> implementation

    Ist vorzuziehen , weil es einige Vorteile hat. implementationStellen Sie die Abhängigkeit zur Erstellungszeit nur für eine Ebene höher bereit (die Abhängigkeit ist zur Laufzeit verfügbar). Als Ergebnis haben Sie einen schnelleren Build (Sie müssen keine Konsumenten neu kompilieren, die höher als 1 Stufe sind).

  • provided -> compileOnly

    Diese Abhängigkeit ist nur zur Kompilierungszeit verfügbar (die Abhängigkeit ist zur Laufzeit nicht verfügbar). Diese Abhängigkeit kann nicht transitiv sein und sein .aar. Es kann mit dem Annotationsprozessor zur Kompilierungszeit verwendet werden und ermöglicht es Ihnen, eine endgültige Ausgabedatei zu reduzieren

  • compile -> annotationProcessor

    Sehr ähnlich, compileOnlygarantiert aber auch, dass die transitive Abhängigkeit für den Verbraucher nicht sichtbar ist

  • apk -> runtimeOnly

    Die Abhängigkeit ist nicht zur Kompilierungszeit verfügbar, sondern zur Laufzeit.

yoAlex5
quelle
Also mit anderen Worten, api = public, implementation = internalund compileOnly = private- ich brauche solche Aliase für diese Funktionen zu erstellen , wie sie Super sind verwirrend.
t3chb0t