Gradle Implementation vs API configuration


232

Sto cercando di capire qual è la differenza tra apie la implementationconfigurazione mentre costruisco le mie dipendenze.
Nella documentazione, dice che implementationha tempi di costruzione migliori, ma, vedendo questo commento in una domanda simile, mi chiedo se sia vero.
Dal momento che non sono un esperto di scuola elementare, spero che qualcuno possa aiutarti. Ho già letto la documentazione ma mi chiedevo una spiegazione di facile comprensione.


1
Hai letto qui ?
MatPag

come di fatto, l'ho fatto, ma, come ho detto, quel commento mi ha fatto meravigliare. quindi ora sono un po 'perso
reinaldomoreira,

Probabilmente cambierete le dipendenze delle vostre librerie da compilea api. Le librerie che usi internamente potrebbero usare alcune implementazioni private che non sono esposte nella libreria finale, quindi sono trasparenti per te. Queste dipendenze "interne-private" possono essere commutate implementatione quando il plug-in Android Gradle compilerà la tua app, salterà la compilazione di tali dipendenze con conseguente minor tempo di costruzione (ma tali dipendenze saranno disponibili in fase di esecuzione). Ovviamente puoi fare la stessa cosa se hai librerie di moduli locali
MatPag

1
Ecco una breve spiegazione grafica di "api" e "implementazione": jeroenmols.com/blog/2017/06/14/androidstudio3
albert c braun,

1
è un post fantastico! grazie @albertbraun
reinaldomoreira il

Risposte:


419

La compileparola chiave Gradle è stata deprecata a favore delle parole chiave apie implementationper configurare le dipendenze.

L'uso apiequivale all'utilizzo del deprecato compile, quindi se si sostituisce tutto compilecon apitutto funzionerà come sempre.

Per comprendere la implementationparola chiave, considerare il seguente esempio.

ESEMPIO

Supponiamo di avere una libreria chiamata MyLibraryche utilizza internamente un'altra libreria chiamata InternalLibrary. Qualcosa come questo:

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

Supponiamo che la configurazione degli MyLibrary build.gradleusi sia così:apidependencies{}

dependencies {
    api project(':InternalLibrary')
}

Vuoi usarlo MyLibrarynel tuo codice, quindi nella tua app build.gradleaggiungi questa dipendenza:

dependencies {
    implementation project(':MyLibrary')
}

Utilizzando la apiconfigurazione (o obsoleto compile) è possibile accedere InternalLibrarynel codice dell'applicazione:

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

In questo modo il modulo MyLibrarypotenzialmente "perde" l'implementazione interna di qualcosa. Non dovresti (essere in grado di) usarlo perché non è importato direttamente da te.

La implementationconfigurazione è stata introdotta per evitarlo. Quindi ora se usi implementationinvece di apiin MyLibrary:

dependencies {
    implementation project(':InternalLibrary')
}

non potrai più chiamare InternalLibrary.giveMeAString()il codice della tua app.

Questo tipo di strategia di boxe consente al plug-in Android Gradle di sapere che se modifichi qualcosa InternalLibrary, deve solo innescare la ricompilazione MyLibrarye non la ricompilazione dell'intera app, perché non hai accesso InternalLibrary.

Quando si hanno molte dipendenze nidificate, questo meccanismo può accelerare molto la compilazione. (Guarda il video collegato alla fine per una piena comprensione di questo)

CONCLUSIONI

  • Quando passi al nuovo plug-in Android Gradle 3.XX, devi sostituire tutto compilecon la implementationparola chiave (1 *) . Quindi prova a compilare e testare la tua app. Se tutto va bene lascia il codice così com'è, se hai problemi probabilmente hai qualcosa di sbagliato nelle tue dipendenze o hai usato qualcosa che ora è privato e non più accessibile. Suggerimento dell'ingegnere del plugin Android Gradle Jerome Dochez (1 ) * )

  • Se sei un manutentore di librerie, dovresti utilizzarlo apiper ogni dipendenza necessaria per l'API pubblica della tua biblioteca, mentre utilizzalo implementationper testare dipendenze o dipendenze che non devono essere utilizzate dagli utenti finali.

Articolo utile In mostra la differenza tra implementazione e api

RIFERIMENTI (Questo è lo stesso video suddiviso per risparmiare tempo)

Google I / O 2017 - Come velocizzare le build Gradle (FULL VIDEO)

Google I / O 2017 - Come accelerare la compilazione di Gradle (SOLO PARTE NEW PLADIN 3.0.0)

Google I / O 2017 - Come accelerare la costruzione di Gradle (riferimento a 1 * )

Documentazione Android


4
Ho notato che API non sembra funzionare bene nei moduli di libreria. Se lo uso, non riesco ancora ad accedere alle dipendenze dal mio progetto di app. Posso solo accedere al codice nella libreria stessa.
Allan W

1
Questo va bene e funziona su build di debug ma quando si utilizza ProGuard (nelle versioni di rilascio) MyLibrary#myString()andrà in crash perché ProGuard sarà InternalLibraryrimosso. Qual è la best practice per le librerie Android da utilizzare nelle app ProGuard?
hardysim,

3
Penso che la risposta non sia precisa, l'applicazione può utilizzare qualsiasi ambito desideri per MyLibrary. Vedrà o no la InternalLibrary a seconda che MyLibrary usi o meno l'implementazione.
Snicolas,

2
grazie uomo. spiegazione fantastica, molto meglio di quella data nei documenti ufficiali di Android
Henry,

2
è una bella spiegazione. Teoria e calcestruzzo si sono mescolati brillantemente. Molto bene. Grazie per quello
Peter Kahn,

134

Mi piace pensare a una apidipendenza come pubblica (vista da altri moduli) mentre implementationdipendenza come privata (vista solo da questo modulo).

Nota che a differenza di public/ privatevariabili e metodi, api/ implementationdipendenze non vengono applicati dal runtime. Questa è semplicemente un'ottimizzazione del tempo di costruzione, che consente Gradledi sapere quali moduli è necessario ricompilare quando una delle dipendenze cambia la sua API.


16
Adoro la semplicità di questa risposta, grazie mille
Kevin Gilles,

2
La vera differenza (AFAICT) è che il file pom generato inserisce apidipendenze nell'ambito "compilazione" (saranno incluse come dipendenze nella libreria e tutto ciò che dipende dalla libreria) e implementationdipendenze nell'ambito "runtime" (è meglio che siano classpath quando il codice è in esecuzione, ma non sono necessari per compilare altro codice che utilizza la libreria).
Shadow Man,

@ShadowMan È un dettaglio di implementazione del plug-in, responsabile della generazione del file POM, del modo in cui mappa gli ambiti Gradle sugli ambiti Maven .
dev.bmax

1
Dovresti usarlo implementationper qualsiasi dipendenza necessaria per l'esecuzione (e per la compilazione della tua libreria), ma non dovrebbe essere automaticamente trascinata in progetti che usano la tua libreria. Un esempio potrebbe essere jax-rs, la tua libreria potrebbe usare RESTeasy, ma non dovrebbe trascinare quelle librerie in nessun progetto che usi la tua libreria, dal momento che potrebbero voler usare Jersey.
Shadow Man,

1
è così che conosci qualcuno che ottiene i suoi contenuti: D grazie per la risposta semplice e chiara
Elias Fazel,

12

Considera di avere un appmodulo che utilizza lib1come libreria e lib1utilizza lib2come libreria. Qualcosa di simile a questo: app -> lib1 -> lib2.

Ora quando si utilizza api lib2in lib1, quindi è app possibile visualizzare i lib2 codici quando si utilizza: api lib1o implementation lib1nel appmodulo.

MA quando si utilizza implementation lib2in lib1, quindi app non è possibile visualizzare i lib2codici.


5

Le risposte di @matpag e @ dev-bmax sono abbastanza chiare da far comprendere agli utenti diversi usi tra implementazione e api. Voglio solo fare una spiegazione extra da un'altra prospettiva, spero di aiutare le persone che hanno la stessa domanda.

Ho creato due progetti per i test:

  • il progetto A come progetto di libreria java chiamato 'frameworks-web-gradle-plugin' dipende da 'org.springframework.boot: spring-boot-gradle-plugin: 1.5.20.RELEASE'
  • il progetto B dipende dal progetto A mediante l'implementazione 'com.example.frameworks.gradle: frameworks-web-gradle-plugin: 0.0.1-SNAPSHOT'

La gerarchia delle dipendenze descritta sopra appare come:

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

Quindi ho testato i seguenti scenari:

  1. Rendere il progetto A dipende da "org.springframework.boot: spring-boot-gradle-plugin: 1.5.20.RELEASE" per implementazione .

    Esegui il gradle dependenciescomando in un terminale nella directory root di poject B , con il seguente screenshot di output possiamo vedere che 'spring-boot-gradle-plugin' appare nell'albero delle dipendenze di runtimeClasspath, ma non in quello di compileClasspath, penso che sia esattamente il motivo per cui non possiamo fare uso della libreria che ha dichiarato usando l'implementazione, semplicemente non lo farà attraverso la compilazione.

    inserisci qui la descrizione dell'immagine

  2. Rendere il progetto A dipende da "org.springframework.boot: spring-boot-gradle-plugin: 1.5.20.RELEASE" di api

    Esegui gradle dependenciesnuovamente il comando in un terminale nella directory principale di Poject B. Ora 'spring-boot-gradle-plugin' appare sia nell'albero delle dipendenze compileClasspath che runtimeClasspath.

    inserisci qui la descrizione dell'immagine

Una differenza significativa che ho notato è che la dipendenza nel progetto produttore / biblioteca dichiarata in modo implementativo non apparirà nel compileClasspath dei progetti consumer, quindi non possiamo usare la lib corrispondente nei progetti consumer.


2

Dalla documentazione Gradle :

Diamo un'occhiata a uno script di build molto semplice per un progetto basato su 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.+'
}

implementazione

Le dipendenze richieste per compilare l'origine di produzione del progetto che non fanno parte dell'API esposta dal progetto. Ad esempio, il progetto utilizza Hibernate per l'implementazione del livello di persistenza interno.

api

Le dipendenze richieste per compilare l'origine di produzione del progetto che fanno parte dell'API esposta dal progetto. Ad esempio, il progetto utilizza Guava ed espone interfacce pubbliche con le classi Guava nelle loro firme dei metodi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.