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

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 las que compilehay implementationy en lugar de las que testCompilehay testImplementation.

Ejemplo:

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

en lugar de

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

¿Cuál es la diferencia entre ellos y qué debería usar?

Answer

tl; dr

Simplemente 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. Se agregó en 3.0 para reemplazar proporcionado y no compilar. ( providedintroducido cuando Gradle no tenía un nombre de configuración para ese caso de uso y lo nombró según el alcance proporcionado por Maven).

Es uno de los cambios más importantes que vienen con el complemento de Android Gradle 3.0 que Google anunció en IO17 .

La compileconfiguración ahora está obsoleta y debería ser reemplazada por implementationoapi

De la documentación de Gradle :

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

Dependencies appearing in the api configurations will be transitively exposed to consumers of the library, and as such will appear on the compile classpath of consumers.

Dependencies found in the implementation configuration will, on the other hand, not be exposed to consumers, and therefore not leak into the consumers' compile classpath. This comes with several benefits:

  • dependencies do not leak into the compile classpath of consumers anymore, so you will never accidentally depend on a transitive dependency
  • faster compilation thanks to reduced classpath size
  • less recompilations when implementation dependencies change: consumers would not need to be recompiled
  • cleaner publishing: when used in conjunction with the new maven-publish plugin, Java libraries produce POM files that distinguish exactly between what is required to compile against the library and what is required to use the library at runtime (in other words, don't mix what is needed to compile the library itself and what is needed to compile against the library).

The compile configuration still exists, but should not be used as it will not offer the guarantees that the api and implementation configurations provide.


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 unos de otros, o si está creando una biblioteca.

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


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

  • app (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 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 dependencias ...

appnecesita consumir :myandroidlibrary, por lo que en appbuild.gradle use 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 verse build.gradle? ¿Qué alcance debemos utilizar?

Tenemos tres opciones:

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

Dependencia3

What's the difference between them and what should I be using?

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

Si está 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)

Dependency5

Si está utilizando la implementationconfiguración, MySecretno se expone.

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 debes elegir? Eso realmente depende de sus requisitos.

Si desea exponer dependencias, use apio compile.

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

Nota:

Esto es solo una parte esencial de las configuraciones de Gradle, consulte la Tabla 49.1. Complemento de biblioteca de 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

CompileLa configuración quedó obsoleta y debería reemplazarse por implementationo api.

Puede leer los documentos en la sección de separación de implementación y API .

La breve parte es

The key difference between the standard Java plugin and the Java Library plugin is that the latter introduces the concept of an API exposed to consumers. A library is a Java component meant to be consumed by other components. It's a very common use case in multi-project builds, but also as soon as you have external dependencies.

The plugin exposes two configurations that can be used to declare dependencies: api and implementation. The api configuration should be used to declare dependencies which are exported by the library API, whereas the implementation configuration should be used to declare dependencies which are internal to the component.

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

Breve solución:

El mejor enfoque es reemplazar todas las compiledependencias por implementationdependencias. Y solo donde se filtra 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 del complemento de Android Gradle 3.0 : teníamos un gran problema: un cambio de código hace que se recompilen todos los módulos. La causa principal de esto es que Gradle no sabe si filtra la interfaz de un módulo a través de otro o no.

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

Como tal, la compiledependencia ha quedado obsoleta y se ha reemplazado por dos nuevas:

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

  • 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

+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| 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.  |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+

Configuración de dependencia de Gradle

Gradle 3.0 introdujo los siguientes cambios:

  • compile -> api

    apiLa palabra clave es la misma que en desuso, lo compile que expone esta dependencia para todos los niveles.

  • compile -> implementation

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

  • provided -> compileOnly

    Esta dependencia está disponible solo 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 utilizar con el procesador de anotaciones en tiempo de compilación [Acerca de] 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.

[Tipo de dependencia de POM]

La breve diferencia en el término lego es:

  • Si está trabajando en una interfaz o módulo que brinda soporte a otros módulos al exponer 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 la dependencia establecida internamente, use 'implementación'.
  • 'compilar' funcionó igual que 'api', sin embargo, si solo está implementando o usando cualquier biblioteca, la 'implementación' funcionará mejor y le ahorrará recursos.

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

Desde la versión 5.6.3, la documentación de Gradle proporciona reglas generales simples para identificar si una compiledependencia anterior (o una nueva) debe reemplazarse con implementationuna apidependencia o una :

  • Prefer the implementation configuration over api when possible

This keeps the dependencies off of the consumer’s compilation classpath. In addition, the consumers will immediately fail to compile if any implementation types accidentally leak into the public API.

So when should you use the api configuration? An API dependency is one that contains at least one type that is exposed in the library binary interface, often referred to as its ABI (Application Binary Interface). This includes, but is not limited to:

  • types used in super classes or interfaces
  • types used in public method parameters, including generic parameter types (where public is something that is visible to compilers. I.e. , public, protected and package private members in the Java world)
  • types used in public fields
  • public annotation types

By contrast, any type that is used in the following list is irrelevant to the ABI, and therefore should be declared as an implementation dependency:

  • types exclusively used in method bodies
  • types exclusively used in private members
  • types exclusively found in internal classes (future versions of Gradle will let you declare which packages belong to the public API)

Algunas notas antes de seguir adelante; La compilación está en desuso y la documentación indica que debe usar la implementación porque la compilación se eliminará en la versión 7.0 de Gradle. Si ejecuta su compilación de Gradle con --warning-mode all, verá el siguiente mensaje;

The compile configuration has been deprecated for dependency declaration. This will fail with an error in Gradle 7.0. Please use the implementation configuration instead.


Con solo mirar la imagen de las páginas de ayuda, tiene mucho sentido.

Entonces tienes las cajas azules compileClasspathy runtimeClassPath.
El compileClasspathes lo que se requiere para hacer una compilación satisfactoria cuando se ejecuta gradle build. Las bibliotecas que estarán presentes en la ruta de clases al compilar serán todas las bibliotecas que estén configuradas en su compilación de gradle usando compileOnlyo implementation.

Luego tenemos runtimeClasspathy esos son todos los paquetes que agregó usando implementationo runtimeOnly. Todas esas bibliotecas se agregarán al archivo de compilación final que implemente en el servidor.

Como también puede ver en la imagen, si desea que una biblioteca se use para la compilación pero también desea que se agregue al archivo de compilación, entonces implementationdebe usarse.

Un ejemplo de runtimeOnlypuede ser un controlador de base de datos.
Un ejemplo de compileOnlycan ser servlet-api.
Un ejemplo de implementationpuede ser un núcleo de resorte.

Gradle

  • implementación: mayormente usamos la configuración de implementación. Oculta la dependencia interna del módulo a su consumidor para evitar el uso accidental de cualquier dependencia transitiva, por lo tanto, una compilación más rápida y menos recompilación.

  • api: debe usarse con mucho cuidado, ya que filtra el classpath de compilación del consumidor, por lo tanto, el uso indebido de api podría generar contaminación por dependencia.

  • compileOnly: cuando no necesitamos ninguna dependencia en tiempo de ejecución, ya que la dependencia compileOnly no se convertirá en parte de la compilación final. obtendremos un tamaño de construcción más pequeño.

  • runtimeOnly: cuando queremos cambiar o intercambiar el comportamiento de la biblioteca en tiempo de ejecución (en la compilación final).

He creado una publicación con una comprensión profunda de cada una con un ejemplo de trabajo: código fuente

https://medium.com/@gauraw.negi/how-gradle-dependency-configurations-work-underhood-e934906752e5

Configuraciones de Gradle