Qual è la differenza tra implementazione e compilazione in Gradle?


1030

Dopo l'aggiornamento ad Android Studio 3.0 e la creazione di un nuovo progetto, ho notato che build.gradlec'è un nuovo modo per aggiungere nuove dipendenze invece che compilec'è implementatione invece che testCompilec'è testImplementation.

Esempio:

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

invece di

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

Qual è la differenza tra loro e cosa dovrei usare?

Risposte:


1282

tl; dr

Basta sostituire:

  • compilecon implementation(se non hai bisogno di transitività) o api(se hai bisogno di transitività)
  • testCompile con testImplementation
  • debugCompile con debugImplementation
  • androidTestCompile con androidTestImplementation
  • compileOnlyè ancora valido. È stato aggiunto in 3.0 per sostituire fornito e non compilare. ( providedintrodotto quando Gradle non aveva un nome di configurazione per quel caso d'uso e lo chiamava come ambito fornito da Maven.)

È uno dei cambiamenti in arrivo con Gradle 3.0 che Google ha annunciato all'IO17 .

La compileconfigurazione è ora obsoleta e deve essere sostituita da implementationoapi

Dalla documentazione Gradle :

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

Le dipendenze che compaiono nelle apiconfigurazioni saranno esposte in modo transitorio ai consumatori della biblioteca e come tali appariranno sul percorso di compilazione della classe dei consumatori.

Le dipendenze rilevate nella implementationconfigurazione, d'altra parte, non saranno esposte ai consumatori, e quindi non penetreranno nel percorso di classe di compilazione dei consumatori. Ciò comporta numerosi vantaggi:

  • le dipendenze non perdono più nel percorso di compilazione dei consumatori, quindi non dipenderai mai accidentalmente da una dipendenza transitiva
  • compilazione più veloce grazie alla dimensione ridotta del percorso di classe
  • meno ricompilazioni quando cambiano le dipendenze dell'implementazione: i consumatori non dovrebbero essere ricompilati
  • pubblicazione più pulita: se utilizzata insieme al nuovo plug-in maven-publishing, le librerie Java producono file POM che distinguono esattamente tra ciò che è richiesto per la compilazione rispetto alla libreria e ciò che è necessario per utilizzare la libreria in fase di esecuzione (in altre parole, non mescolare ciò che è necessario per compilare la libreria stessa e ciò che è necessario per compilare contro la libreria).

La configurazione di compilazione esiste ancora, ma non dovrebbe essere utilizzata in quanto non offrirà le garanzie fornite dalle configurazioni apie implementation.


Nota: se stai usando solo una libreria nel tuo modulo app - il caso comune - non noterai alcuna differenza.
vedrai la differenza solo se hai un progetto complesso con moduli che dipendono l'uno dall'altro o se stai creando una libreria.


137
Chi sono i "consumatori"?
Suragch,

34
il consumatore è il modulo che utilizza la libreria. nel caso di Android, è l'applicazione Android. Penso che sia chiaro e non sono sicuro se questo è quello che stai chiedendo.
impazzito l'

21
Questo è quello che sembrava anche a me. Ma se sto creando una libreria, ovviamente voglio che la sua API sia esposta all'app. Altrimenti, come utilizzerebbe la mia libreria lo sviluppatore dell'app? Ecco perché non ho il significato di implementationnascondere la dipendenza. La mia domanda ha un senso?
Suragch,

235
sì, ora ha senso se l'app dipende dalla libreria x che a sua volta dipende da y, z. se usi implementationsolo x api sarà esposto, ma se usi apiy, anche z sarà esposto.
impazzito l'

36
Fatto! Adesso ha più senso. Puoi aggiungere questa spiegazione alla tua risposta. È più chiaro della documentazione citata.
Suragch,

380

Questa risposta sarà dimostrare la differenza tra implementation, apie compilesu un progetto.


Diciamo che ho un progetto con tre moduli Gradle:

  • app (un'applicazione Android)
  • myandroidlibrary (una libreria Android)
  • myjavalibrary (una libreria Java)

appha myandroidlibrarycome dipendenze. myandroidlibraryha myjavalibrary come dipendenze.

Dependency1

myjavalibraryha una MySecretclasse

public class MySecret {

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

myandroidlibraryha MyAndroidComponentclasse che manipola il valore dalla MySecretclasse.

public class MyAndroidComponent {

    private static String component = MySecret.getSecret();

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

Infine, appè interessato solo al valore dimyandroidlibrary

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

Ora parliamo di dipendenze ...

appbisogno di consumare :myandroidlibrary, quindi appnell'uso build.gradle implementation.

( Nota : puoi usare anche api / compilare. Ma tieni questo pensiero per un momento.)

dependencies {
    implementation project(':myandroidlibrary')      
}

Dependency2

Come pensi myandroidlibraryche dovrebbe apparire build.gradle? Quale ambito dovremmo usare?

Abbiamo tre opzioni:

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

Dependency3

Qual è la differenza tra loro e cosa dovrei usare?

Compila o API (opzione # 2 o # 3) Dependency4

Se stai usando compileo api. La nostra applicazione Android ora è in grado di accedere alla myandroidcomponentdipendenza, che è una MySecretclasse.

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

Implementazione (opzione n. 1)

Dependency5

Se stai usando la implementationconfigurazione, MySecretnon è esposto.

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

Quindi, quale configurazione dovresti scegliere? Dipende molto dalle tue esigenze.

Se vuoi esporre le dipendenze usa apio compile.

Se non si desidera esporre dipendenze (nascondere il modulo interno), utilizzare implementation.

Nota:

Questa è solo una sintesi delle configurazioni Gradle, fare riferimento alla Tabella 49.1. Plug-in Java Library: configurazioni utilizzate per dichiarare dipendenze per una spiegazione più dettagliata.

Il progetto di esempio per questa risposta è disponibile su https://github.com/aldoKelvianto/ImplementationVsCompile


1
Ho aggiunto dipendenza a un file jar usando l'implementazione, se non è esposto l'accesso ad esso perché sono ancora in grado di ottenere e il mio codice funziona bene?
smkrn110

L'implementazione di @ smkrn110 esporrà la tua libreria jar, ma non le librerie delle dipendenze jar.
aldok,

2
@WijaySharma la risposta accettata afferma che compilenon garantisce le stesse cose che apigarantisce.
Risorse secondarie 6

9
Penso che questa dovrebbe essere la risposta accettata. Ben spiegato!
Shashank Kapsime,

9
@ StevenW.Klassen è il downvote più immeritato di cui abbia mai sentito parlare. Se ritieni che l'ordine delle informazioni non sia ottimale, suggerisci una modifica invece di lamentarti
Tim

65

Compilela configurazione era obsoleta e deve essere sostituita da implementationo api.

Puoi leggere i documenti su https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_separation .

La breve parte è-

La differenza chiave tra il plug-in Java standard e il plug-in Java Library è che quest'ultimo introduce il concetto di API esposta ai consumatori. Una libreria è un componente Java destinato ad essere utilizzato da altri componenti. È un caso d'uso molto comune nelle build multi-progetto, ma anche non appena si hanno dipendenze esterne.

Il plugin espone due configurazioni che possono essere utilizzate per dichiarare dipendenze: api e implementazione. La configurazione API dovrebbe essere utilizzata per dichiarare le dipendenze che vengono esportate dall'API della libreria, mentre la configurazione dell'implementazione dovrebbe essere utilizzata per dichiarare dipendenze interne al componente.

Per ulteriori spiegazioni fare riferimento a questa immagine. Breve spiegazione


46

Breve soluzione:

L'approccio migliore è sostituire tutte le compiledipendenze con implementationdipendenze. E solo dove perdi l'interfaccia di un modulo, dovresti usare api. Ciò dovrebbe causare una ricompilazione molto minore.

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

Spiega meglio:

Prima del plug-in Android Gradle 3.0 : abbiamo avuto un grosso problema che è una modifica del codice che causa la ricompilazione di tutti i moduli. La causa principale di ciò è che Gradle non sa se si perde l'interfaccia di un modulo attraverso un altro o no.

Dopo il plug-in Android Gradle 3.0 : l'ultimo plug-in Android Gradle ora richiede di definire esplicitamente se si perde l'interfaccia di un modulo. In base a ciò, può fare la scelta giusta su ciò che dovrebbe ricompilare.

Come tale il compile dipendenza è stata deprecata e sostituita da due nuove:

  • api: perdi l'interfaccia di questo modulo attraverso la tua stessa interfaccia, il che significa esattamente lo stesso della vecchia compiledipendenza

  • implementation: usi questo modulo solo internamente e non lo perde attraverso la tua interfaccia

Quindi ora puoi esplicitamente dire a Gradle di ricompilare un modulo se l'interfaccia di un modulo usato cambia o no.

Per gentile concessione del blog Jeroen Mols


2
Spiegazione chiara e concisa. Grazie!
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.  |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+

Non risponde direttamente alla domanda
skryvets

1
C'è anche uno sviluppo: Solo
Hohenheimsenberg,

Cosa devo usare se ho bisogno sia di runtime che di tempo di compilazione? Attualmente, ho implementationseguito un runtime.
Maroun,

8

La breve differenza nel termine laico è:

  • Se stai lavorando su un'interfaccia o un modulo che fornisce supporto ad altri moduli esponendo i membri della dipendenza dichiarata, dovresti usare 'api'.
  • Se stai creando un'applicazione o un modulo che sta per implementare o utilizzare internamente la dipendenza dichiarata, usa 'implementazione'.
  • 'compile' ha funzionato come 'api', tuttavia, se stai implementando o usando solo una libreria, 'implementazione' funzionerà meglio e ti farà risparmiare risorse.

leggi la risposta di @aldok per un esempio completo.


Ma il fatto è che se una persona è venuta deliberatamente qui a cercare la risposta a queste domande, dopo tutto non è un laico.
Rishav

6

Dalla versione 5.6.3 la documentazione Gradle fornisce semplici regole pratiche per identificare se una vecchia compiledipendenza (o una nuova) deve essere sostituita con una implementationo una apidipendenza:

  • Preferisci la implementationconfigurazione apiquando possibile

Ciò mantiene le dipendenze al di fuori del percorso di classe di compilazione del consumatore. Inoltre, i consumatori non riusciranno immediatamente a compilare se eventuali tipi di implementazione perdono accidentalmente nell'API pubblica.

Quindi, quando dovresti usare la apiconfigurazione? Una dipendenza API è quella che contiene almeno un tipo esposto nell'interfaccia binaria della libreria, spesso definita ABI (Application Binary Interface). Questo include, ma non è limitato a:

  • tipi utilizzati in super classi o interfacce
  • tipi utilizzati nei parametri dei metodi pubblici, inclusi i tipi di parametri generici (dove pubblico è qualcosa che è visibile ai compilatori. Cioè, pubblico, protetto e pacchetto membri privati ​​nel mondo Java)
  • tipi utilizzati in campi pubblici
  • tipi di annotazioni pubbliche

Al contrario, qualsiasi tipo utilizzato nel seguente elenco è irrilevante per l'ABI e pertanto deve essere dichiarato come implementation dipendenza:

  • tipi utilizzati esclusivamente negli organismi metodo
  • tipi utilizzati esclusivamente nei membri privati
  • tipi trovati esclusivamente nelle classi interne (le versioni future di Gradle ti consentiranno di dichiarare quali pacchetti appartengono all'API pubblica)

6

Gradle 3.0 introdotto le prossime modifiche:

  • compile -> api

    api la parola chiave è uguale a obsoleta compile

  • compile -> implementation

    È preferibile perché ha alcuni vantaggi. implementationesporre la dipendenza solo per un livello superiore al momento della compilazione (la dipendenza è disponibile in fase di esecuzione). Di conseguenza hai una build più veloce (non è necessario ricompilare i consumatori che sono più alti di 1 livello in alto)

  • provided -> compileOnly

    Questa dipendenza è disponibile solo in fase di compilazione (la dipendenza non è disponibile in fase di esecuzione). Questa dipendenza non può essere transitiva ed essere .aar. Può essere utilizzato con il processore di annotazione in fase di compilazione e consente di ridurre un file di output finale

  • compile -> annotationProcessor

    Molto simile compileOnlyma garantisce anche che la dipendenza transitiva non è visibile per il consumatore

  • apk -> runtimeOnly

    La dipendenza non è disponibile in fase di compilazione ma è disponibile in fase di esecuzione.


Quindi, in altre parole, api = public, implementation = internale compileOnly = private- ho bisogno di creare tali alias per queste funzioni in quanto sono super-confusione.
t3chb0t
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.