Implémentation Gradle vs configuration API

232

J'essaie de comprendre quelle est la différence entre apiet la implementationconfiguration lors de la construction de mes dépendances.
Dans la documentation, il est dit que le implementationtemps de construction est meilleur, mais, en voyant ce commentaire dans une question similaire, je me demande si c'est vrai.
Étant donné que je ne suis pas un expert du gradle, j'espère que quelqu'un pourra vous aider. J'ai déjà lu la documentation mais je me demandais une explication facile à comprendre.

reinaldomoreira
la source
1
Avez-vous lu ici ?
MatPag
comme en fait, je l'ai fait, mais, comme je l'ai dit, ce commentaire m'a fait réfléchir. donc je suis un peu perdu maintenant
reinaldomoreira
Vous passerez probablement vos dépendances de bibliothèques de compileà api. Les bibliothèques que vous utilisez en interne peuvent utiliser des implémentations privées qui ne sont pas exposées dans la bibliothèque finale afin qu'elles soient transparentes pour vous. Ces dépendances "internes-privées" peuvent être commutées implementationet lorsque le plugin Android Gradle compilera votre application, il ignorera la compilation de ces dépendances, ce qui réduira le temps de construction (mais ces dépendances seront disponibles au moment de l'exécution). Évidemment, vous pouvez faire la même chose si vous avez des bibliothèques de modules locales
MatPag
1
Voici une courte explication graphique de «api» et «implémentation»: jeroenmols.com/blog/2017/06/14/androidstudio3
albert c braun
1
c'est un post génial! merci @albertbraun
reinaldomoreira

Réponses:

419

Le compilemot clé Gradle a été déconseillé au profit des mots clés apiet implementationpour configurer les dépendances.

Utiliser apiéquivaut à utiliser le obsolète compile, donc si vous remplacez tout compilepar apitout, cela fonctionnera comme toujours.

Pour comprendre le implementationmot - clé, considérons l'exemple suivant.

EXEMPLE

Supposons que vous ayez une bibliothèque appelée MyLibraryqui utilise en interne une autre bibliothèque appelée InternalLibrary. Quelque chose comme ça:

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

Supposons que la configuration MyLibrary build.gradleutilise comme ceci:apidependencies{}

dependencies {
    api project(':InternalLibrary')
}

Vous voulez utiliser MyLibrarydans votre code donc dans votre application build.gradlevous ajoutez cette dépendance:

dependencies {
    implementation project(':MyLibrary')
}

En utilisant la apiconfiguration (ou obsolète compile), vous pouvez accéder InternalLibrarydans votre code d'application:

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

De cette façon, le module MyLibrary«fuit» potentiellement l'implémentation interne de quelque chose. Vous ne devriez pas (pouvoir) l'utiliser car il n'est pas directement importé par vous.

La implementationconfiguration a été introduite pour éviter cela. Alors maintenant, si vous utilisez implementationau lieu de apidans MyLibrary:

dependencies {
    implementation project(':InternalLibrary')
}

vous ne pourrez plus appeler InternalLibrary.giveMeAString()votre code d'application.

Ce type de stratégie de boxe permet au plugin Android Gradle de savoir que si vous modifiez quelque chose InternalLibrary, il ne doit déclencher que la recompilation MyLibraryet non la recompilation de l'ensemble de votre application, car vous n'y avez pas accès InternalLibrary.

Lorsque vous avez beaucoup de dépendances imbriquées, ce mécanisme peut accélérer considérablement la construction. (Regardez la vidéo liée à la fin pour une compréhension complète de cela)

CONCLUSIONS

  • Lorsque vous passez au nouveau plugin Android Gradle 3.XX, vous devez remplacer tous vos fichiers compilepar le implementationmot - clé (1 *) . Essayez ensuite de compiler et de tester votre application. Si tout va bien, laissez le code tel quel, si vous avez des problèmes, vous avez probablement un problème avec vos dépendances ou vous avez utilisé quelque chose qui est maintenant privé et qui n'est pas plus accessible. Suggestion par l'ingénieur du plugin Android Gradle Jerome Dochez (1 ) * )

  • Si vous êtes un responsable de bibliothèque, vous devez utiliser apipour chaque dépendance qui est nécessaire pour l'API publique de votre bibliothèque, tandis que implementationpour les dépendances de test ou les dépendances qui ne doivent pas être utilisées par les utilisateurs finaux.

Article utile présentant la différence entre l' implémentation et l' API

RÉFÉRENCES (Il s'agit de la même vidéo divisée pour gagner du temps)

Google I / O 2017 - Comment accélérer la construction de Gradle (FULL VIDEO)

Google I / O 2017 - Comment accélérer la construction de Gradle (NOUVEAU GRADLE PLUGIN 3.0.0 PART UNIQUEMENT)

Google I / O 2017 - Comment accélérer la construction de Gradle (référence à 1 * )

Documentation Android

MatPag
la source
4
J'ai remarqué que l'api ne semble pas bien fonctionner dans les modules de bibliothèque. Si je l'utilise, je ne peux toujours pas accéder aux dépendances de mon projet d'application. Je ne peux accéder qu'au code dans cette bibliothèque elle-même.
Allan W
1
C'est très bien et fonctionne sur les versions de débogage, mais lors de l'utilisation de ProGuard (sur les versions-versions), il MyLibrary#myString()se bloquera car ProGuard aura été InternalLibrarysupprimé. Quelle est la meilleure pratique pour les bibliothèques Android à utiliser dans les applications ProGuard?
hardysim
3
Je pense que la réponse n'est pas exacte, l'application peut utiliser la portée qu'elle souhaite pour MyLibrary. Il verra ou non la bibliothèque interne selon que la bibliothèque virtuelle utilise ou non l'implémentation.
Snicolas
2
Merci mec. explication impressionnante, bien meilleure que celle donnée dans les documents officiels d'Android
Henry
2
c'est une belle explication. Théorie et béton se mélangent avec brio. Bien joué. Merci pour ça
Peter Kahn
134

J'aime penser une apidépendance comme publique (vue par d'autres modules) tandis que la implementationdépendance est privée (vue seulement par ce module).

Notez que, contrairement aux public/ privatevariables et aux méthodes, api/ les implementationdépendances ne sont pas appliquées par le runtime. Il s'agit simplement d'une optimisation au moment de la construction, qui permet Gradlede savoir de quels modules il a besoin pour recompiler lorsque l'une des dépendances change son API.

dev.bmax
la source
16
J'adore la simplicité de cette réponse merci beaucoup
Kevin Gilles
2
La vraie différence (AFAICT) est que le fichier pom généré place les apidépendances dans la portée "compiler" (elles seront incluses en tant que dépendances dans votre bibliothèque et tout ce qui dépend de votre bibliothèque) et les implementationdépendances dans la portée "runtime" (il vaut mieux être sur le classpath lorsque votre code est en cours d'exécution, mais ils ne sont pas nécessaires pour compiler un autre code qui utilise votre bibliothèque).
Shadow Man
@ShadowMan C'est un détail d'implémentation du plugin, chargé de générer le fichier POM, comment il mappe les portées Gradle aux portées Maven .
dev.bmax
1
Vous devez utiliser implementationpour toute dépendance nécessaire à l'exécution (et pour votre bibliothèque à compiler), mais cela ne doit pas être automatiquement tiré dans des projets qui utilisent votre bibliothèque. Un exemple serait jax-rs, votre bibliothèque pourrait utiliser RESTeasy, mais elle ne devrait pas extraire ces bibliothèques dans un projet qui utilise votre bibliothèque, car elles pourraient vouloir utiliser Jersey à la place.
Shadow Man
1
c'est ainsi que vous savez que quelqu'un obtient ses affaires: D merci pour la réponse simple et claire
Elias Fazel
12

Considérez que vous avez un appmodule qui utilise lib1comme bibliothèque et lib1utiliselib2 comme bibliothèque. Quelque chose comme ceci: app -> lib1 -> lib2.

Maintenant, lorsque vous utilisez api lib2dans lib1, vous app pouvez voir les lib2 codes lorsque vous utilisez: api lib1ouimplementation lib1 dans leapp module.

MAIS lors de l'utilisation implementation lib2dans lib1, app ne peut pas voir les lib2codes.

Ehsan Mashhadi
la source
5

Réponses de @matpag et @ dev-bmax sont suffisamment claires pour faire comprendre aux utilisateurs les différents usages entre l'implémentation et l'API. Je veux juste faire une explication supplémentaire sous un autre angle, espère aider les gens qui ont la même question.

J'ai créé deux projets à tester:

  • projet A tant que projet de bibliothèque java nommé 'frameworks-web-gradle-plugin' dépend de 'org.springframework.boot: spring-boot-gradle-plugin: 1.5.20.RELEASE'
  • le projet B dépend du projet A par implémentation 'com.example.frameworks.gradle: frameworks-web-gradle-plugin: 0.0.1-SNAPSHOT'

La hiérarchie des dépendances décrite ci-dessus ressemble à:

[project-b] -> [project-a] -> [spring-boot-gradle-plugin]

J'ai ensuite testé les scénarios suivants:

  1. Faire le projet A dépend de 'org.springframework.boot: spring-boot-gradle-plugin: 1.5.20.RELEASE' par l' implémentation .

    Exécutez la gradle dependenciescommande dans un terminal dans le répertoire racine poject B , avec la capture d'écran suivante de la sortie, nous pouvons voir que 'spring-boot-gradle-plugin' apparaît dans l'arbre des dépendances de runtimeClasspath, mais pas dans celui de compileClasspath, je pense que c'est exactement pourquoi nous ne pouvons pas faire utilisation de la bibliothèque qui a déclaré utiliser l'implémentation, elle ne le sera tout simplement pas par compilation.

    entrez la description de l'image ici

  2. Faire un projet A dépend de 'org.springframework.boot: spring-boot-gradle-plugin: 1.5.20.RELEASE' par api

    Exécutez à nouveau la gradle dependenciescommande dans un terminal dans le répertoire racine de poject B. Maintenant, 'spring-boot-gradle-plugin' apparaît à la fois dans l'arborescence des dépendances compileClasspath et runtimeClasspath.

    entrez la description de l'image ici

Une différence significative que j'ai remarquée est que la dépendance dans le projet producteur / bibliothèque déclarée de manière implémentée n'apparaîtra pas dans compileClasspath des projets consommateurs, de sorte que nous ne pouvons pas utiliser la bibliothèque correspondante dans les projets consommateurs.

Rong.l
la source
2

De la documentation Gradle :

Voyons un script de construction très simple pour un projet basé sur 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.+'
}

la mise en oeuvre

Les dépendances requises pour compiler la source de production du projet qui ne font pas partie de l'API exposée par le projet. Par exemple, le projet utilise Hibernate pour sa mise en œuvre de la couche de persistance interne.

api

Les dépendances requises pour compiler la source de production du projet qui font partie de l'API exposée par le projet. Par exemple, le projet utilise Guava et expose les interfaces publiques avec les classes Guava dans leurs signatures de méthode.

Camilo Silva
la source