Implementación de Gradle vs configuración de API

232

Estoy tratando de averiguar cuál es la diferencia apiy la implementationconfiguración mientras construyo mis dependencias.
En la documentación, dice que implementationtiene un mejor tiempo de compilación, pero al ver este comentario en una pregunta similar, me pregunto si es cierto.
Como no soy un experto en gradle, espero que alguien pueda ayudar. Ya leí la documentación pero me preguntaba acerca de una explicación fácil de entender.

reinaldomoreira
fuente
1
¿Has leído aquí ?
MatPag
de hecho, lo hice, pero, como dije, ese comentario me hizo pensar al respecto. así que estoy un poco perdido ahora
reinaldomoreira
Probablemente cambie las dependencias de sus bibliotecas de compilea api. Las bibliotecas que usa internamente podrían usar algunas implementaciones privadas que no están expuestas en la biblioteca final, por lo que son transparentes para usted. Esas dependencias "internas-privadas" se pueden cambiar implementationy cuando el complemento de gradle de Android compile su aplicación, omitirá la compilación de esas dependencias, lo que dará como resultado un tiempo de compilación menor (pero esas dependencias estarán disponibles en tiempo de ejecución). Obviamente, puede hacer lo mismo si tiene bibliotecas de módulos locales
MatPag
1
Aquí hay una breve explicación gráfica de 'api' e 'implementación': jeroenmols.com/blog/2017/06/14/androidstudio3
albert c braun
1
¡Esa es una publicación increíble! gracias @albertbraun
reinaldomoreira

Respuestas:

419

La compilepalabra clave Gradle quedó en desuso a favor de las palabras clave apiy implementationpara configurar las dependencias.

Usar apies el equivalente a usar lo obsoleto compile, por lo que si reemplazas todo compilecon apitodo funcionará como siempre.

Para comprender la implementationpalabra clave, considere el siguiente ejemplo.

EJEMPLO

Supongamos que tiene una biblioteca llamada MyLibraryque usa internamente otra biblioteca llamada InternalLibrary. Algo como esto:

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

Supongamos que la configuración de MyLibrary build.gradleusos es así:apidependencies{}

dependencies {
    api project(':InternalLibrary')
}

Desea usar MyLibraryen su código para que en su aplicación build.gradleagregue esta dependencia:

dependencies {
    implementation project(':MyLibrary')
}

Usando la apiconfiguración (o en desuso compile) puede acceder InternalLibraryen su código de aplicación:

// 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 esta manera, el módulo MyLibraryestá potencialmente "filtrando" la implementación interna de algo. No deberías (poder) usar eso porque no lo has importado directamente.

La implementationconfiguración se introdujo para evitar esto. Entonces, si usa en implementationlugar de apien MyLibrary:

dependencies {
    implementation project(':InternalLibrary')
}

ya no podrá llamar InternalLibrary.giveMeAString()a su código de aplicación.

Este tipo de estrategia de boxeo le permite al complemento Android Gradle saber que si edita algo InternalLibrary, solo debe activar la recompilación MyLibraryy no la recompilación de toda su aplicación, porque no tiene acceso a ella InternalLibrary.

Cuando tiene muchas dependencias anidadas, este mecanismo puede acelerar mucho la compilación. (Mire el video vinculado al final para obtener una comprensión completa de esto)

CONCLUSIONES

  • Cuando cambie al nuevo complemento Android Gradle 3.XX, debe reemplazar todo compilecon la implementationpalabra clave (1 *) . Luego intente compilar y probar su aplicación. Si todo está bien, deje el código como está, si tiene problemas, probablemente tenga algo mal con sus dependencias o haya utilizado algo que ahora es privado y no más accesible. Sugerencia del ingeniero del complemento Android Gradle Jerome Dochez (1 ) * )

  • Si usted es un mantenedor de la biblioteca, debe usarlo apipara cada dependencia que sea necesaria para la API pública de su biblioteca, mientras lo usa implementationpara probar dependencias o dependencias que no deben ser utilizadas por los usuarios finales.

Artículo útil que muestra la diferencia entre la implementación y la API

REFERENCIAS (Este es el mismo video dividido para ahorrar tiempo)

Google I / O 2017: cómo acelerar las compilaciones de Gradle (VIDEO COMPLETO)

Google I / O 2017 - Cómo acelerar las compilaciones de Gradle (NUEVA PARTE GRADLE PLUGIN 3.0.0 SOLAMENTE)

Google I / O 2017: cómo acelerar las compilaciones de Gradle (referencia a 1 * )

Documentación de Android

MatPag
fuente
44
Me di cuenta de que la API no parece funcionar bien en los módulos de la biblioteca. Si lo uso, aún no puedo acceder a las dependencias de mi proyecto de aplicación. Solo puedo acceder al código en esa biblioteca.
Allan W
1
Esto está bien y funciona en las compilaciones de depuración, pero cuando se usa ProGuard (en las versiones de lanzamiento) MyLibrary#myString()se bloqueará porque ProGuard se habrá InternalLibraryeliminado. ¿Cuál es la mejor práctica para que las bibliotecas de Android se utilicen en las aplicaciones ProGuard?
hardysim
3
Creo que la respuesta no es precisa, la aplicación puede usar el alcance que quiera para MyLibrary. Verá o no InternalLibrary dependiendo de si MyLibrary usa o no api / implementación.
Snicolas
2
gracias hombre. explicación increíble, mucho mejor que la que se da en los documentos oficiales de Android
Henry
2
Esa es una hermosa explicación. La teoría y el concreto se mezclan brillantemente. Bien hecho. Gracias por eso
Peter Kahn
134

Me gusta pensar en una apidependencia como pública (vista por otros módulos) mientras que la implementationdependencia como privada (solo vista por este módulo).

Tenga en cuenta que, a diferencia de public/ privatevariables y métodos, api/ implementationdependencias no se aplican por el tiempo de ejecución. Esto es simplemente una optimización en tiempo de construcción, que permite Gradlesaber qué módulos necesita recompilar cuando una de las dependencias cambia su API.

dev.bmax
fuente
16
Me encanta la simplicidad de esta respuesta, muchas gracias
Kevin Gilles
2
La verdadera diferencia (AFAICT) es que el archivo pom generado coloca las apidependencias en el ámbito de "compilación" (se incluirán como dependencias en su biblioteca y todo lo que dependa de su biblioteca) y implementationdependencias en el ámbito de "tiempo de ejecución" (es mejor que estén en el classpath cuando su código se está ejecutando, pero no son necesarios para compilar otro código que use su biblioteca).
Shadow Man el
@ShadowMan Es un detalle de implementación del complemento, responsable de generar el archivo POM, cómo asigna los ámbitos de Gradle a los ámbitos de Maven .
dev.bmax
1
Debería usarlo implementationpara cualquier dependencia que sea necesaria para ejecutar (y para que su biblioteca se compile), pero eso no debería ser automáticamente incorporado a los proyectos que usan su biblioteca. Un ejemplo sería jax-rs, su biblioteca podría usar RESTeasy, pero no debería arrastrar esas librerías a ningún proyecto que use su biblioteca, ya que podrían querer usar Jersey en su lugar.
Shadow Man el
1
así es como sabes que alguien obtiene sus cosas: D gracias por una respuesta simple y clara
Elias Fazel
12

Considere que tiene un appmódulo que se usa lib1como biblioteca y que se lib1usa lib2como biblioteca. Algo como esto: app -> lib1 -> lib2.

Ahora, cuando se usa api lib2in lib1, app puede ver los lib2 códigos cuando se usa: api lib1o implementation lib1en el appmódulo.

PERO cuando se usa implementation lib2en lib1, entonces app no puede ver los lib2códigos.

Ehsan Mashhadi
fuente
5

Las respuestas de @matpag y @ dev-bmax son lo suficientemente claras como para que las personas entiendan los diferentes usos entre la implementación y la API. Solo quiero hacer una explicación adicional desde otro ángulo, espero ayudar a las personas que tienen la misma pregunta.

Creé dos proyectos para probar:

  • el proyecto A como un proyecto de biblioteca java llamado 'frameworks-web-gradle-plugin' depende de 'org.springframework.boot: spring-boot-gradle-plugin: 1.5.20.RELEASE'
  • el proyecto B depende del proyecto A mediante la implementación 'com.example.frameworks.gradle: frameworks-web-gradle-plugin: 0.0.1-SNAPSHOT'

La jerarquía de dependencias descrita anteriormente se ve así:

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

Luego probé los siguientes escenarios:

  1. Hacer el proyecto A depende de 'org.springframework.boot: spring-boot-gradle-plugin: 1.5.20.RELEASE' por implementación .

    Ejecute el gradle dependenciescomando en un terminal en el directorio raíz del objeto B, con la siguiente captura de pantalla de salida podemos ver que 'spring-boot-gradle-plugin' aparece en el árbol de dependencias runtimeClasspath, pero no en compileClasspath's, creo que es exactamente por eso que no podemos hacer uso de la biblioteca que declaró usar la implementación, simplemente no lo hará a través de la compilación.

    ingrese la descripción de la imagen aquí

  2. Hacer que el proyecto A dependa de 'org.springframework.boot: spring-boot-gradle-plugin: 1.5.20.RELEASE' por api

    Ejecute el gradle dependenciescomando en una terminal en el directorio raíz del objeto B nuevamente. Ahora aparece 'spring-boot-gradle-plugin' en el árbol de dependencias compileClasspath y runtimeClasspath.

    ingrese la descripción de la imagen aquí

Una diferencia significativa que noté es que la dependencia en el proyecto productor / biblioteca declarada en forma de implementación no aparecerá en compileClasspath de proyectos de consumo, por lo que no podemos hacer uso de la lib correspondiente en los proyectos de consumo.

Rong.l
fuente
2

De la documentación de gradle :

Echemos un vistazo a un script de compilación muy simple para un proyecto basado en 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.+'
}

implementación

Las dependencias requeridas para compilar la fuente de producción del proyecto que no son parte de la API expuesta por el proyecto. Por ejemplo, el proyecto usa Hibernate para su implementación de capa de persistencia interna.

api

Las dependencias necesarias para compilar la fuente de producción del proyecto que forman parte de la API expuesta por el proyecto. Por ejemplo, el proyecto usa Guava y expone interfaces públicas con clases de Guava en sus firmas de métodos.

Camilo Silva
fuente