Implementação Gradle x configuração da API

231

Estou tentando descobrir qual é a diferença entre apie implementationconfiguração ao criar minhas dependências.
Na documentação, ele diz que implementationtem melhor tempo de construção, mas, vendo esse comentário em uma pergunta semelhante, fiquei me perguntando se isso é verdade.
Como não sou especialista em graduação, espero que alguém possa ajudar. Eu já li a documentação, mas estava pensando em uma explicação fácil de entender.

reinaldomoreira
fonte
1
Você leu aqui ?
MatPag
na verdade, eu fiz, mas, como eu disse, esse comentário fez maravilha. então eu estou meio perdido agora
reinaldomoreira
Você provavelmente mudará as dependências de suas bibliotecas de compilepara api. As bibliotecas usadas internamente podem usar algumas implementações privadas que não são expostas na biblioteca final para que sejam transparentes para você. Essas dependências "interno-privadas" podem ser alteradas implementatione, quando o plug-in do Android gradle compilar seu aplicativo, ele ignorará a compilação dessas dependências, resultando em um tempo de compilação menor (mas essas dependências estarão disponíveis no tempo de execução). Obviamente, você pode fazer a mesma coisa se você tem bibliotecas de módulos locais
MatPag
1
Aqui está uma explicação gráfica curta de 'api' e 'implementação': jeroenmols.com/blog/2017/06/14/androidstudio3
albert c Braun
1
esse é um post incrível! Obrigado @albertbraun
reinaldomoreira

Respostas:

418

A compilepalavra-chave Gradle foi descontinuada em favor das palavras-chave apie implementationpara configurar dependências.

Usar apié o equivalente a usar o obsoleto compile, portanto, se você substituir todos compilepor apitudo, tudo funcionará como sempre.

Para entender a implementationpalavra - chave, considere o seguinte exemplo.

EXEMPLO

Suponha que você tenha uma biblioteca chamada MyLibraryque internamente use outra biblioteca chamada InternalLibrary. Algo assim:

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

Suponha que a configuração de MyLibrary build.gradleusos seja assim:apidependencies{}

dependencies {
    api project(':InternalLibrary')
}

Você deseja usar MyLibraryno seu código e, no aplicativo, build.gradleadicionar esta dependência:

dependencies {
    implementation project(':MyLibrary')
}

Usando a apiconfiguração (ou obsoleta compile), você pode acessar InternalLibraryno código do seu aplicativo:

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

Dessa maneira, o módulo MyLibraryestá potencialmente "vazando" a implementação interna de algo. Você não deve poder usá-lo porque não é importado diretamente por você.

A implementationconfiguração foi introduzida para evitar isso. Portanto, agora se você usa implementationem vez de apiem MyLibrary:

dependencies {
    implementation project(':InternalLibrary')
}

você não poderá mais acessar InternalLibrary.giveMeAString()o código do seu aplicativo.

Esse tipo de estratégia de boxe permite que o plug-in Android Gradle saiba que, se você editar algo InternalLibrary, ele deve acionar a recompilação MyLibrarye não a recompilação de todo o aplicativo, porque você não tem acesso InternalLibrary.

Quando você tem muitas dependências aninhadas, esse mecanismo pode acelerar muito a compilação. (Assista ao vídeo no final do link para uma compreensão completa disso)

CONCLUSÕES

  • Ao mudar para o novo plug-in Android Gradle 3.XX, você deve substituir todo o seu compilepela implementationpalavra - chave (1 *) . Em seguida, tente compilar e testar seu aplicativo. Se tudo estiver ok, deixe o código como está, se você tiver problemas, provavelmente há algo errado com suas dependências ou usou algo que agora é privado e não é mais acessível. Sugestão do engenheiro de plug-in Android Gradle Jerome Dochez (1 ) * )

  • Se você é um mantenedor de biblioteca, deve usar apipara todas as dependências necessárias para a API pública da sua biblioteca, enquanto usa implementationpara dependências de teste ou dependências que não devem ser usadas pelos usuários finais.

Artigo útil Mostrando a diferença entre implementação e API

REFERÊNCIAS (este é o mesmo vídeo dividido para economizar tempo)

E / S do Google 2017 - Como acelerar a construção do Gradle (VÍDEO COMPLETO)

Google I / O 2017 - Como acelerar a criação do Gradle (SOMENTE NOVA GRADLE PLUGIN 3.0.0)

Google I / O 2017 - Como acelerar a criação do Gradle (referência a 1 * )

Documentação do Android

MatPag
fonte
4
Notei que a API não parece funcionar bem nos módulos da biblioteca. Se eu usá-lo, ainda não consigo acessar as dependências do meu projeto de aplicativo. Só posso acessar o código nessa biblioteca.
Allan W
1
Isso é bom e funciona em compilações de depuração, mas ao usar o ProGuard (em versões de lançamento) MyLibrary#myString()falha porque o ProGuard foi InternalLibraryremovido. Qual é a melhor prática para que as libs do Android sejam usadas nos aplicativos ProGuard?
hardysim
3
Eu acho que a resposta não é precisa, o aplicativo pode usar qualquer escopo que desejar para a MyLibrary. Ele verá ou não a InternalLibrary, dependendo de a MyLibrary usar ou não API / implementação.
Snicolas
2
obrigado cara. explicação incrível, muito melhor do que aquela dada na documentação oficial do Android
Henry
2
essa é uma explicação bonita. Teoria e concreto se misturavam brilhantemente. Bem feito. Obrigado por isso
Peter Kahn
134

Eu gosto de pensar em uma apidependência como pública (vista por outros módulos) enquanto a implementationdependência como privada (vista somente por este módulo).

Observe que diferentemente de public/ privatevariáveis ​​e métodos api/ implementationdependências não são impostos pelo tempo de execução. Isso é apenas uma otimização em tempo de construção, que permite Gradlesaber quais módulos ele precisa recompilar quando uma das dependências altera sua API.

dev.bmax
fonte
16
Amo a simplicidade desta resposta muito obrigado
Kevin Gilles
2
A diferença real (AFAICT) é que o arquivo pom gerado coloca apidependências no escopo "compilação" (elas serão incluídas como dependências na sua biblioteca e tudo o que depende da sua biblioteca) e implementationdependências no escopo "tempo de execução" (é melhor que estejam no diretório classpath quando seu código estiver em execução, mas eles não são necessários para compilar outro código que usa sua biblioteca).
Shadow Man
@ShadowMan É um detalhe de implementação do plugin, responsável por gerar o arquivo POM, como ele mapeia os escopos Gradle para escopos Maven .
precisa saber é o seguinte
1
Você deve usar implementationpara qualquer dependência necessária para executar (e para a sua biblioteca compilar), mas isso não deve ser automaticamente puxado para projetos que usam sua biblioteca. Um exemplo seria jax-rs, sua biblioteca pode usar RESTeasy, mas não deve puxar essas bibliotecas para qualquer projeto que use sua biblioteca, pois elas podem usar Jersey.
Shadow Man
1
é assim que você sabe que alguém obter suas coisas: D obrigado por simples e resposta clara
Elias Fazel
12

Considere que você possui um appmódulo que usa lib1como biblioteca e lib1usa lib2como biblioteca. Algo parecido com isto: app -> lib1 -> lib2.

Agora, ao usar api lib2in lib1, é app possível ver lib2 códigos ao usar: api lib1ou implementation lib1no appmódulo.

Mas quando se utiliza implementation lib2em lib1, então app não pode ver os lib2códigos.

Ehsan Mashhadi
fonte
5

As respostas do @matpag e do dev-bmax são claras o suficiente para fazer as pessoas entenderem usos diferentes entre implementação e API. Eu só quero fazer uma explicação extra de outro ângulo, na esperança de ajudar as pessoas que têm a mesma pergunta.

Criei dois projetos para teste:

  • O projeto A como um projeto de biblioteca java chamado 'frameworks-web-gradle-plugin' depende de 'org.springframework.boot: spring-boot-gradle-plugin: 1.5.20.RELEASE'
  • o projeto B depende do projeto A pela implementação 'com.example.frameworks.gradle: frameworks-web-gradle-plugin: 0.0.1-SNAPSHOT'

A hierarquia de dependências descrita acima se parece com:

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

Depois testei os seguintes cenários:

  1. Tornar o projeto A depende de 'org.springframework.boot: spring-boot-gradle-plugin: 1.5.20.RELEASE' pela implementação .

    Execute o gradle dependenciescomando em um terminal no diretório raiz do poject B , com a captura de tela da saída a seguir. Podemos ver que 'spring-boot-gradle-plugin' aparece na árvore de dependências runtimeClasspath, mas não na compileClasspath, acho que é exatamente por isso que não podemos criar uso de biblioteca que declarou usando implementação, simplesmente não será através de compilação.

    insira a descrição da imagem aqui

  2. O projeto A depende do 'org.springframework.boot: spring-boot-gradle-plugin: 1.5.20.RELEASE' da api

    Execute o gradle dependenciescomando em um terminal no diretório raiz do poject B novamente. Agora 'spring-boot-gradle-plugin' aparece na árvore de dependências compileClasspath e runtimeClasspath.

    insira a descrição da imagem aqui

Uma diferença significativa que notei é que a dependência no projeto produtor / biblioteca declarada no modo de implementação não aparecerá no compileClasspath de projetos do consumidor, para que não possamos usar a lib correspondente nos projetos do consumidor.

Rong.l
fonte
2

Da documentação gradle :

Vamos dar uma olhada em um script de construção muito simples para um projeto baseado em 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.+'
}

implementação

As dependências necessárias para compilar a fonte de produção do projeto que não faz parte da API exposta pelo projeto. Por exemplo, o projeto usa o Hibernate para sua implementação interna da camada de persistência.

api

As dependências necessárias para compilar a fonte de produção do projeto que faz parte da API exposta pelo projeto. Por exemplo, o projeto usa o Guava e expõe interfaces públicas com as classes do Guava em suas assinaturas de método.

Camilo Silva
fonte