Quelle est la différence entre l'implémentation et la compilation dans Gradle?

1030

Après la mise à jour vers Android Studio 3.0 et la création d'un nouveau projet, j'ai remarqué qu'il build.gradleexiste une nouvelle façon d'ajouter de nouvelles dépendances au lieu de compilece qui existe implementationet au lieu de testCompilecela testImplementation.

Exemple:

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

au lieu de

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

Quelle est la différence entre eux et que dois-je utiliser?

humazed
la source

Réponses:

1282

tl; dr

Remplacez simplement:

  • compileavec implementation(si vous n'avez pas besoin de transitivité) ou api(si vous avez besoin de transitivité)
  • testCompile avec testImplementation
  • debugCompile avec debugImplementation
  • androidTestCompile avec androidTestImplementation
  • compileOnlyest toujours valide. Il a été ajouté en 3.0 pour remplacer fourni et ne pas compiler. ( providedintroduit lorsque Gradle n'avait pas de nom de configuration pour ce cas d'utilisation et le nommait d'après la portée fournie par Maven.)

C'est l'un des changements les plus marquants de Gradle 3.0 que Google a annoncé à IO17 .

La compileconfiguration est désormais obsolète et doit être remplacée par implementationouapi

De la documentation Gradle :

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

Les dépendances apparaissant dans les apiconfigurations seront transitoirement exposées aux consommateurs de la bibliothèque, et en tant que telles apparaîtront sur le chemin de classe de compilation des consommateurs.

Les dépendances trouvées dans la implementationconfiguration, en revanche, ne seront pas exposées aux consommateurs, et ne fuiront donc pas dans le chemin de classe de compilation des consommateurs. Cela comporte plusieurs avantages:

  • les dépendances ne s'infiltrent plus dans le chemin de classe de compilation des consommateurs, vous ne dépendrez donc jamais accidentellement d'une dépendance transitive
  • compilation plus rapide grâce à une taille de chemin de classe réduite
  • moins de recompilations lorsque les dépendances d'implémentation changent: les consommateurs n'auraient pas besoin d'être recompilés
  • publication plus propre: lorsqu'elles sont utilisées en conjonction avec le nouveau plugin maven-publish, les bibliothèques Java produisent des fichiers POM qui distinguent exactement ce qui est nécessaire pour compiler avec la bibliothèque et ce qui est requis pour utiliser la bibliothèque au moment de l'exécution (en d'autres termes, ne le faites pas mélanger ce qui est nécessaire pour compiler la bibliothèque elle-même et ce qui est nécessaire pour compiler avec la bibliothèque).

La configuration de compilation existe toujours, mais ne doit pas être utilisée car elle n'offre pas les garanties fournies par les configurations apiet implementation.


Remarque: si vous utilisez uniquement une bibliothèque dans votre module d'application - le cas commun - vous ne remarquerez aucune différence.
vous ne verrez la différence que si vous avez un projet complexe avec des modules qui dépendent les uns des autres ou si vous créez une bibliothèque.

humazed
la source
137
Qui sont les "consommateurs"?
Suragch
34
le consommateur est le module utilisant la bibliothèque. dans le cas d'Android, c'est l'application Android. Je pense que c'est clair et je ne sais pas si c'est ce que vous demandez.
humazed
21
C'est à ça que ça ressemblait aussi pour moi. Mais si je crée une bibliothèque, je veux bien sûr que son API soit exposée à l'application. Sinon, comment le développeur de l'application utiliserait-il ma bibliothèque? C'est pourquoi je n'ai pas le sens de implementationcacher la dépendance. Ma question a-t-elle un sens?
Suragch
235
oui, cela a du sens maintenant, si votre application dépend de la bibliothèque x qui dépend elle-même de y, z. si vous utilisez implementationuniquement x api sera exposé, mais si vous utilisez apiy, z sera également exposé.
humazed
36
Je l'ai! Cela a plus de sens maintenant. Vous pouvez ajouter cette explication dans votre réponse. C'est plus clair que la documentation citée.
Suragch
380

Cette réponse démontrera la différence entre implementation, apiet compilesur un projet.


Disons que j'ai un projet avec trois modules Gradle:

  • application (une application Android)
  • myandroidlibrary (une bibliothèque Android)
  • myjavalibrary (une bibliothèque Java)

appa myandroidlibrarycomme dépendances. myandroidlibrarya myjavalibrary comme dépendances.

Dépendance1

myjavalibrarya une MySecretclasse

public class MySecret {

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

myandroidlibrarya une MyAndroidComponentclasse qui manipule la valeur de la MySecretclasse.

public class MyAndroidComponent {

    private static String component = MySecret.getSecret();

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

Enfin, appne s'intéresse qu'à la valeur demyandroidlibrary

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

Parlons maintenant des dépendances ...

appbesoin de consommer :myandroidlibrary, donc dans appbuild.gradle use implementation.

( Remarque : Vous pouvez également utiliser api / compile. Mais maintenez cette pensée pendant un moment.)

dependencies {
    implementation project(':myandroidlibrary')      
}

Dépendance2

À quoi myandroidlibrarydevrait ressembler build.gradle? Quelle portée utiliser?

Nous avons trois options:

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

Dépendance3

Quelle est la différence entre eux et que dois-je utiliser?

Compiler ou Api (option # 2 ou # 3) Dépendance4

Si vous utilisez compileou api. Notre application Android peut désormais accéder à la myandroidcomponentdépendance, qui est une MySecretclasse.

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

Mise en œuvre (option # 1)

Dépendance5

Si vous utilisez la implementationconfiguration, MySecretn'est pas exposé.

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

Alors, quelle configuration choisir? Cela dépend vraiment de vos besoins.

Si vous souhaitez exposer les dépendances, utilisez apiou compile.

Si vous ne souhaitez pas exposer les dépendances (masquer votre module interne), utilisez implementation.

Remarque:

Ceci n'est qu'un aperçu des configurations Gradle, reportez-vous au tableau 49.1. Plugin Java Library - configurations utilisées pour déclarer les dépendances pour une explication plus détaillée.

L'exemple de projet pour cette réponse est disponible sur https://github.com/aldoKelvianto/ImplementationVsCompile

aldok
la source
1
J'ai ajouté une dépendance à un fichier jar en utilisant l'implémentation, si ce n'est pas exposer l'accès à celui-ci pourquoi je suis toujours en mesure d'obtenir et que mon code fonctionne bien?
smkrn110
L'implémentation de @ smkrn110 exposera votre bibliothèque jar, mais pas vos bibliothèques de dépendances jar.
aldok
2
@WijaySharma la réponse acceptée indique que compilene garantit pas les mêmes choses que les apigaranties.
Sub 6 Resources
9
Je pense que cela devrait être la réponse acceptée. Bien expliqué!
Shashank Kapsime
9
@ StevenW.Klassen qui est le downvote le plus immérité dont j'ai jamais entendu parler. Si vous pensez que l'ordre des informations n'est pas optimal, proposez une modification au lieu de vous en plaindre
Tim
65

Compilela configuration est obsolète et doit être remplacée par implementationou api.

Vous pouvez lire les documents sur https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_separation .

La brève partie étant-

La principale différence entre le plugin Java standard et le plugin Java Library est que ce dernier introduit le concept d'une API exposée aux consommateurs. Une bibliothèque est un composant Java destiné à être consommé par d'autres composants. C'est un cas d'utilisation très courant dans les builds multi-projets, mais aussi dès que vous avez des dépendances externes.

Le plugin expose deux configurations qui peuvent être utilisées pour déclarer des dépendances: l'api et l'implémentation. La configuration api doit être utilisée pour déclarer les dépendances qui sont exportées par l'API de bibliothèque, tandis que la configuration d'implémentation doit être utilisée pour déclarer les dépendances qui sont internes au composant.

Pour plus d'explications, reportez-vous à cette image. Brève explication

Rishav
la source
46

Brève solution:

La meilleure approche consiste à remplacer toutes les compiledépendances par des implementationdépendances. Et seulement lorsque vous perdez l'interface d'un module, vous devez utiliser api. Cela devrait entraîner beaucoup moins de recompilation.

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

Explique plus:

Avant le plugin Android Gradle 3.0 : nous avons eu un gros problème qui est qu'un changement de code entraîne la recompilation de tous les modules. La cause principale de ceci est que Gradle ne sait pas si vous fuyez l'interface d'un module à travers un autre ou non.

Après le plug-in Android Gradle 3.0 : le dernier plug-in Android Gradle vous oblige désormais à définir explicitement si vous perdez l'interface d'un module. Sur cette base, il peut faire le bon choix sur ce qu'il doit recompiler.

En tant que telle, la compiledépendance a été dépréciée et remplacée par deux nouvelles:

  • api: vous fuyez l'interface de ce module via votre propre interface, ce qui signifie exactement la même chose que l'ancienne compiledépendance

  • implementation: vous n'utilisez ce module qu'en interne et ne le faites pas passer par votre interface

Alors maintenant, vous pouvez explicitement dire à Gradle de recompiler un module si l'interface d'un module utilisé change ou non.

Avec l'aimable autorisation du blog Jeroen Mols

Shayan Amani
la source
2
Explication claire et concise. Merci!
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
la source
Ne répond pas directement à la question
skryvets
1
Il y a aussi un développementOnly
Hohenheimsenberg
Que dois-je utiliser si j'ai besoin à la fois de temps d'exécution et de compilation? Actuellement, j'ai implementationsuivi par un runtime.
Maroun
8

La brève différence dans le terme de profane est:

  • Si vous travaillez sur une interface ou un module qui prend en charge d'autres modules en exposant les membres de la dépendance déclarée, vous devez utiliser 'api'.
  • Si vous créez une application ou un module qui va implémenter ou utiliser la dépendance indiquée en interne, utilisez «implémentation».
  • 'compiler' fonctionnait de la même manière que 'api', cependant, si vous implémentez ou utilisez uniquement une bibliothèque, 'implémentation' fonctionnera mieux et vous économisera des ressources.

lisez la réponse de @aldok pour un exemple complet.

Rushabh Agarwal
la source
Mais le fait est que si une personne vient délibérément ici pour chercher la réponse à ces questions, elle n'est pas un profane après tout.
Rishav
6

Depuis la version 5.6.3, la documentation Gradle fournit des règles de base simples pour identifier si une ancienne compiledépendance (ou une nouvelle) doit être remplacée par une implementationou une apidépendance:

  • Préférez la implementationconfiguration plutôt apique possible

Cela maintient les dépendances hors du chemin de classe de compilation du consommateur. De plus, les consommateurs ne parviendront pas immédiatement à compiler si des types d'implémentation fuient accidentellement dans l'API publique.

Alors, quand devriez-vous utiliser la apiconfiguration? Une dépendance API est celle qui contient au moins un type qui est exposé dans l'interface binaire de la bibliothèque, souvent appelée son ABI (Application Binary Interface). Cela comprend, mais sans s'y limiter:

  • types utilisés dans les super classes ou interfaces
  • types utilisés dans les paramètres de méthode publique, y compris les types de paramètres génériques (où public est quelque chose qui est visible pour les compilateurs. C'est-à-dire, public, protégé et empaquetez les membres privés dans le monde Java)
  • types utilisés dans les domaines publics
  • types d'annotation publique

En revanche, tout type utilisé dans la liste suivante n'est pas pertinent pour l'ABI et doit donc être déclaré en tant que implementationdépendance:

  • types exclusivement utilisés dans les corps de méthode
  • types exclusivement utilisés dans les membres privés
  • types exclusivement trouvés dans les classes internes (les futures versions de Gradle vous permettront de déclarer quels packages appartiennent à l'API publique)
Pom12
la source
6

Gradle 3.0 introduit les changements suivants:

  • compile -> api

    api mot-clé est le même que obsolète compile

  • compile -> implementation

    Est préférable , car présente certains avantages. implementationexposer la dépendance uniquement pour un niveau au moment de la génération (la dépendance est disponible au moment de l'exécution). En conséquence, vous avez une construction plus rapide (pas besoin de recompiler les consommateurs qui sont supérieurs à 1 niveau)

  • provided -> compileOnly

    Cette dépendance est disponible uniquement au moment de la compilation (la dépendance n'est pas disponible au moment de l'exécution). Cette dépendance ne peut pas être transitive et l'être .aar. Il peut être utilisé avec un processeur d'annotation de temps de compilation et vous permet de réduire un fichier de sortie final

  • compile -> annotationProcessor

    Très similaire compileOnlymais garantit également que la dépendance transitive n'est pas visible pour le consommateur

  • apk -> runtimeOnly

    La dépendance n'est pas disponible au moment de la compilation mais disponible au moment de l'exécution.

yoAlex5
la source
En d' autres termes, api = public, implementation = internalet compileOnly = private- je dois créer ces alias pour ces fonctions car ils sont super confusion.
t3chb0t