¿Cuál es la diferencia entre implementación y compilación en Gradle?

1030

Después de actualizar a Android Studio 3.0 y crear un nuevo proyecto, noté que build.gradlehay una nueva forma de agregar nuevas dependencias en lugar de compileexistir implementationy en lugar de testCompileexistir testImplementation.

Ejemplo:

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

en vez de

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

¿Cuál es la diferencia entre ellos y qué debo usar?

humillado
fuente

Respuestas:

1282

tl; dr

Solo reemplace:

  • compilecon implementation(si no necesita transitividad) o api(si necesita transitividad)
  • testCompile con testImplementation
  • debugCompile con debugImplementation
  • androidTestCompile con androidTestImplementation
  • compileOnlyaun es válido. Fue agregado en 3.0 para reemplazar provisto y no compilar. ( providedintroducido cuando Gradle no tenía un nombre de configuración para ese caso de uso y lo nombró por el alcance proporcionado por Maven).

Es uno de los cambios importantes que viene con Gradle 3.0 que Google anunció en IO17 .

La compileconfiguración ahora está en desuso y debe reemplazarse por implementationoapi

De la documentación de Gradle :

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

Las dependencias que aparecen en las apiconfiguraciones estarán expuestas de forma transitiva a los consumidores de la biblioteca y, como tales, aparecerán en la compilación classpath de los consumidores.

Las dependencias encontradas en la implementationconfiguración, por otro lado, no estarán expuestas a los consumidores y, por lo tanto, no se filtrarán en la ruta de compilación de los consumidores. Esto viene con varios beneficios:

  • las dependencias ya no se filtran en el classpath de compilación de los consumidores, por lo que nunca dependerá accidentalmente de una dependencia transitiva
  • compilación más rápida gracias al tamaño reducido de classpath
  • menos recompilaciones cuando cambian las dependencias de implementación: los consumidores no necesitarían ser recompilados
  • Publicación más limpia: cuando se usa junto con el nuevo complemento de publicación de Maven, las bibliotecas Java producen archivos POM que distinguen exactamente entre lo que se requiere para compilar contra la biblioteca y lo que se requiere para usar la biblioteca en tiempo de ejecución (en otras palabras, no mezclar lo que se necesita para compilar la biblioteca en sí y lo que se necesita para compilar contra la biblioteca).

Todavía existe la configuración de compilación, pero no se debe utilizar, ya que no ofrece las garantías de que los apiy implementationlas configuraciones proporcionan.


Nota: si solo está utilizando una biblioteca en el módulo de su aplicación, el caso común, no notará ninguna diferencia.
solo verá la diferencia si tiene un proyecto complejo con módulos que dependen uno del otro, o si está creando una biblioteca.

humillado
fuente
137
¿Quiénes son los "consumidores"?
Suragch
34
El consumidor es el módulo que utiliza la biblioteca. en el caso de Android, es la aplicación de Android. Creo que esto está claro y no estoy seguro de si esto es lo que está pidiendo.
Humazed
21
A mí también me pareció así. Pero si estoy haciendo una biblioteca, por supuesto, quiero que su API esté expuesta a la aplicación. De lo contrario, ¿cómo usaría el desarrollador de la aplicación mi biblioteca? Es por eso que no entiendo el significado de implementationocultar la dependencia. ¿Mi pregunta tiene sentido?
Suragch
235
Sí, tiene sentido ahora, si su aplicación depende de la biblioteca x que a su vez depende de y, z. si usa implementationsolo x api estará expuesto, pero si usa apiy, z también estará expuesto.
Humazed
36
¡Entendido! Eso tiene mas sentido ahora. Puede agregar esta explicación a su respuesta. Es más claro que la documentación citada.
Suragch
380

Esta respuesta va a demostrar la diferencia entre implementation, apiy compileen un proyecto.


Digamos que tengo un proyecto con tres módulos Gradle:

  • aplicación (una aplicación de Android)
  • myandroidlibrary (una biblioteca de Android)
  • myjavalibrary (una biblioteca de Java)

apptiene myandroidlibrarycomo dependencias. myandroidlibrarytiene myjavalibrary como dependencias.

Dependencia1

myjavalibrarytiene una MySecretclase

public class MySecret {

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

myandroidlibrarytiene una MyAndroidComponentclase que manipula el valor de la MySecretclase.

public class MyAndroidComponent {

    private static String component = MySecret.getSecret();

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

Por último, appsolo le interesa el valor demyandroidlibrary

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

Ahora, hablemos de las dependencias ...

appnecesita consumir :myandroidlibrary, así que en appbuild.gradle uso implementation.

( Nota : también puedes usar api / compile. Pero mantén ese pensamiento por un momento).

dependencies {
    implementation project(':myandroidlibrary')      
}

Dependencia2

¿Cómo crees que myandroidlibrarydebería ser build.gradle? ¿Qué alcance debemos usar?

Tenemos tres opciones:

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

Dependencia3

¿Cuál es la diferencia entre ellos y qué debo usar?

Compilar o API (opción # 2 o # 3) Dependencia4

Si estás usando compileo api. Nuestra aplicación de Android ahora puede acceder a la myandroidcomponentdependencia, que es una MySecretclase.

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

Implementación (opción # 1)

Dependencia5

Si está utilizando la implementationconfiguración, MySecretno está expuesto.

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

Entonces, ¿qué configuración debe elegir? Eso realmente depende de su requerimiento.

Si desea exponer dependencias use apio compile.

Si no desea exponer dependencias (ocultar su módulo interno), use implementation.

Nota:

Esto es solo una esencia de las configuraciones de Gradle, consulte la Tabla 49.1. Complemento de la Biblioteca Java: configuraciones utilizadas para declarar dependencias para una explicación más detallada.

El proyecto de muestra para esta respuesta está disponible en https://github.com/aldoKelvianto/ImplementationVsCompile

aldok
fuente
1
He agregado dependencia a un archivo jar usando la implementación, si no se expone el acceso a él, ¿por qué todavía puedo obtenerlo y mi código funciona bien?
smkrn110
La implementación de @ smkrn110 expondrá su biblioteca jar, pero no sus bibliotecas de dependencias jar.
aldok
2
@WijaySharma la respuesta aceptada afirma que compileno garantiza las mismas cosas que apigarantiza.
Sub 6 Resources
99
Creo que esta debería ser la respuesta aceptada. ¡Bien explicado!
Shashank Kapsime
99
@ StevenW.Klassen, ese es el voto negativo más inmerecido del que he oído hablar. Si cree que el orden de la información no es óptimo, sugiera una edición en lugar de quejarse
Tim
65

Compilela configuración quedó en desuso y debería reemplazarse por implementationo api.

Puede leer los documentos en https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_separation .

La breve parte es-

La diferencia clave entre el complemento estándar de Java y el complemento de la Biblioteca Java es que este último introduce el concepto de una API expuesta a los consumidores. Una biblioteca es un componente de Java destinado a ser consumido por otros componentes. Es un caso de uso muy común en las compilaciones de proyectos múltiples, pero también tan pronto como tenga dependencias externas.

El complemento expone dos configuraciones que se pueden usar para declarar dependencias: api e implementación. La configuración de la API debe usarse para declarar las dependencias que exporta la API de la biblioteca, mientras que la configuración de implementación debe usarse para declarar las dependencias internas del componente.

Para más explicaciones, consulte esta imagen. Breve explicacion

Rishav
fuente
46

Breve solución:

El mejor enfoque es reemplazar todas las compiledependencias con implementationdependencias. Y solo cuando pierda la interfaz de un módulo, debe usar api. Eso debería causar mucha menos recompilación.

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

Explica más:

Antes de Android Gradle plugin 3.0 : tuvimos un gran problema, un cambio de código hace que todos los módulos se vuelvan a compilar. La causa raíz de esto es que Gradle no sabe si se pierde la interfaz de un módulo a través de otro o no.

Después del complemento Android Gradle 3.0 : el último complemento Android Gradle ahora requiere que defina explícitamente si pierde la interfaz de un módulo. En base a eso, puede tomar la decisión correcta sobre lo que debería compilar.

Como tal, la compiledependencia ha quedado en desuso y ha sido reemplazada por dos nuevas:

  • api: pierde la interfaz de este módulo a través de su propia interfaz, lo que significa exactamente lo mismo que la antigua compiledependencia

  • implementation: solo usa este módulo internamente y no lo filtra a través de su interfaz

Entonces, ahora puede decirle explícitamente a Gradle que recompile un módulo si la interfaz de un módulo usado cambia o no.

Cortesía del blog de Jeroen Mols.

Shayan Amani
fuente
2
Explicación limpia y concisa. ¡Gracias!
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
fuente
No responde la pregunta directamente
skryvets
1
También hay un desarrollo Solo
Hohenheimsenberg
¿Qué debo usar si necesito tiempo de ejecución y tiempo de compilación? Actualmente, he implementationseguido por a runtime.
Maroun
8

La breve diferencia en el término de laico es:

  • Si está trabajando en una interfaz o módulo que brinda soporte a otros módulos al exponer a los miembros de la dependencia indicada, debería usar 'api'.
  • Si está creando una aplicación o módulo que va a implementar o usar internamente la dependencia indicada, use 'implementación'.
  • 'compilar' funcionó igual que 'api', sin embargo, si solo está implementando o utilizando cualquier biblioteca, 'implementación' funcionará mejor y le ahorrará recursos.

lea la respuesta de @aldok para obtener un ejemplo completo.

Rushabh Agarwal
fuente
Pero la cosa es que si una persona vino deliberadamente aquí buscando la respuesta a estas preguntas, entonces no es un laico después de todo.
Rishav
6

Desde la versión 5.6.3, la documentación de Gradle proporciona reglas básicas simples para identificar si una compiledependencia antigua (o una nueva) debe reemplazarse con una implementationo una apidependencia:

  • Prefiere la implementationconfiguración sobre apicuando sea posible

Esto mantiene las dependencias fuera del classpath de compilación del consumidor. Además, los consumidores no podrán compilar de inmediato si algún tipo de implementación se filtra accidentalmente en la API pública.

Entonces, ¿cuándo deberías usar la apiconfiguración? Una dependencia API es aquella que contiene al menos un tipo que está expuesto en la interfaz binaria de la biblioteca, a menudo denominada ABI (interfaz binaria de aplicación). Esto incluye, pero no se limita a:

  • tipos utilizados en superclases o interfaces
  • tipos utilizados en parámetros de métodos públicos, incluidos los tipos de parámetros genéricos (donde public es algo que es visible para los compiladores. Es decir, miembros públicos, protegidos y privados de paquetes en el mundo Java)
  • tipos utilizados en campos públicos
  • tipos de anotaciones públicas

Por el contrario, cualquier tipo que se use en la siguiente lista es irrelevante para el ABI y, por lo tanto, debe declararse como una implementationdependencia:

  • tipos utilizados exclusivamente en cuerpos de métodos
  • tipos utilizados exclusivamente en miembros privados
  • tipos encontrados exclusivamente en clases internas (las versiones futuras de Gradle le permitirán declarar qué paquetes pertenecen a la API pública)
Pom12
fuente
6

Gradle 3.0 introdujo los siguientes cambios:

  • compile -> api

    api la palabra clave es la misma que en desuso compile

  • compile -> implementation

    Es preferible porque tiene algunas ventajas. implementationexponer la dependencia solo para un nivel superior en el momento de la compilación (la dependencia está disponible en tiempo de ejecución). Como resultado, tiene una compilación más rápida (no es necesario volver a compilar los consumidores que son superiores a 1 nivel)

  • provided -> compileOnly

    Esta dependencia solo está disponible en tiempo de compilación (la dependencia no está disponible en tiempo de ejecución). Esta dependencia no puede ser transitiva y ser .aar. Se puede usar con el procesador de anotaciones en tiempo de compilación y le permite reducir un archivo de salida final

  • compile -> annotationProcessor

    Muy similar compileOnlypero también garantiza que la dependencia transitiva no sea visible para el consumidor

  • apk -> runtimeOnly

    La dependencia no está disponible en tiempo de compilación pero está disponible en tiempo de ejecución.

yoAlex5
fuente
Así, en otras palabras, api = public, implementation = internaly compileOnly = private- que necesito para crear este tipo de alias para estas funciones, ya que son confusas súper.
t3chb0t