Qual é a diferença entre implementação e compilação no Gradle?


1030

Após atualizar para o Android Studio 3.0 e criar um novo projeto, notei que build.gradleexiste uma nova maneira de adicionar novas dependências em vez de compileexistir implementatione em vez de testCompileexistir testImplementation.

Exemplo:

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

ao invés de

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

Qual a diferença entre eles e o que devo usar?

Respostas:


1282

tl; dr

Apenas substitua:

  • compilecom implementation(se você não precisar de transitividade) ouapi (se você precisar de transitividade)
  • testCompile com testImplementation
  • debugCompile com debugImplementation
  • androidTestCompile com androidTestImplementation
  • compileOnlyainda é válido. Foi adicionado no 3.0 para substituir o fornecido e não compilar. ( providedintroduzido quando Gradle não tinha um nome de configuração para esse caso de uso e o nomeou após o escopo fornecido pelo Maven.)

É uma das mudanças que estão chegando com o Gradle 3.0 que o Google anunciou no IO17 .

A compileconfiguração agora está obsoleta e deve ser substituída por implementationouapi

Na documentação do Gradle :

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

As dependências que aparecem nas apiconfigurações serão expostas transitivamente aos consumidores da biblioteca e, como tal, aparecerão no caminho de classe de compilação dos consumidores.

As dependências encontradas na implementationconfiguração, por outro lado, não serão expostas aos consumidores e, portanto, não vazarão no caminho de classe de compilação dos consumidores. Isso vem com vários benefícios:

  • dependências não vazam mais no caminho de classe de compilação dos consumidores, portanto você nunca dependerá acidentalmente de uma dependência transitiva
  • compilação mais rápida graças ao tamanho reduzido do caminho de classe
  • menos recompilações quando as dependências da implementação mudam: os consumidores não precisariam ser recompilados
  • publicação mais limpa: quando usadas em conjunto com o novo plug-in maven-publish, as bibliotecas Java produzem arquivos POM que distinguem exatamente entre o que é necessário para compilar na biblioteca e o que é necessário para usar a biblioteca em tempo de execução (em outras palavras, não misture o que é necessário para compilar a própria biblioteca e o que é necessário para compilar na biblioteca).

A configuração de compilação ainda existe, mas não deve ser usada, pois não oferecerá as garantias que as configurações apie implementationfornecem.


Nota: se você estiver usando apenas uma biblioteca em seu módulo de aplicativo, o caso comum, não notará nenhuma diferença.
você verá a diferença apenas se tiver um projeto complexo com módulos dependendo um do outro ou se estiver criando uma biblioteca.


137
Quem são os "consumidores"?
Suragch 8/09/17

34
o consumidor é o módulo usando a biblioteca. no caso do Android, é o aplicativo Android. Acho que isso está claro e não tenho certeza se é isso que você está pedindo.
humazed

21
Isso é o que me pareceu também. Mas se estou criando uma biblioteca, é claro que quero que sua API seja exposta ao aplicativo. Caso contrário, como o desenvolvedor de aplicativos usaria minha biblioteca? É por isso que não entendo implementationocultar a dependência. Minha pergunta faz sentido?
Suragch

235
sim, faz sentido agora, se seu aplicativo depende da biblioteca x, que depende de y, z. se você usar implementationapenas x api será exposto, mas se você usar apiy, z também será exposto.
humazed

36
Entendi! Isso faz mais sentido agora. Você pode adicionar esta explicação à sua resposta. É mais claro que a documentação citada.
Suragch

380

Esta resposta vai demonstrar a diferença entre implementation, apie compileem um projeto.


Digamos que eu tenho um projeto com três módulos Gradle:

  • app (um aplicativo Android)
  • myandroidlibrary (uma biblioteca Android)
  • myjavalibrary (uma biblioteca Java)

apptem myandroidlibrarycomo dependências. myandroidlibrarytem myjavalibrary como dependências.

Dependency1

myjavalibrarytem uma MySecretclasse

public class MySecret {

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

myandroidlibrarytem MyAndroidComponentclasse que manipula valor da MySecretclasse

public class MyAndroidComponent {

    private static String component = MySecret.getSecret();

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

Por fim, appestá interessado apenas no valor demyandroidlibrary

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

Agora, vamos falar sobre dependências ...

appprecisa consumir :myandroidlibrary, portanto, no appuso build.gradle implementation.

( Nota : você também pode usar api / compile. Mas mantenha esse pensamento por um momento.)

dependencies {
    implementation project(':myandroidlibrary')      
}

Dependência2

Como você acha que o myandroidlibrarybuild.gradle deve ser? Qual escopo devemos usar?

Temos três opções:

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

Dependência3

Qual a diferença entre eles e o que devo usar?

Compilar ou API (opção 2 ou 3) Dependência4

Se você estiver usando compileou api. Nosso aplicativo Android agora pode acessar a myandroidcomponentdependência, que é uma MySecretclasse.

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

Implementação (opção 1)

Dependência5

Se você estiver usando a implementationconfiguração, MySecretnão será exposto.

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

Então, qual configuração você deve escolher? Isso realmente depende da sua exigência.

Se você deseja expor dependências, use apiou compile.

Se você não deseja expor dependências (ocultando seu módulo interno), useimplementation .

Nota:

Esta é apenas uma essência das configurações de Gradle, consulte a Tabela 49.1. Plug-in da Biblioteca Java - configurações usadas para declarar dependências para obter explicações mais detalhadas.

O projeto de amostra para esta resposta está disponível em https://github.com/aldoKelvianto/ImplementationVsCompile


1
Eu adiciono dependência a um arquivo jar usando a implementação. Se ele não expõe o acesso a ele, por que ainda consigo obter e meu código está funcionando bem?
smkrn110

A implementação do @ smkrn110 irá expor sua biblioteca jar, mas não as bibliotecas de dependências jar.
Aldok #

2
@WijaySharma a resposta aceita afirma que compilenão garante as mesmas coisas que apigarante.
6 Recursos

9
Eu acho que essa deve ser a resposta aceita. Bem explicado!
Shashank Kapsime

9
@ StevenW.Klassen, que é o voto negativo mais imerecido que eu já ouvi falar. Se você acha que a ordem de informações não é o ideal, sugerem uma edição em vez de reclamar sobre isso
Tim

65

CompileA configuração foi descontinuada e deve ser substituída por implementationou api.

Você pode ler os documentos em https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_separation .

A breve parte sendo

A principal diferença entre o plug-in Java padrão e o plug-in Java Library é que este último introduz o conceito de uma API exposta aos consumidores. Uma biblioteca é um componente Java destinado a ser consumido por outros componentes. É um caso de uso muito comum em compilações de vários projetos, mas também assim que você tiver dependências externas.

O plug-in expõe duas configurações que podem ser usadas para declarar dependências: API e implementação. A configuração da API deve ser usada para declarar dependências que são exportadas pela API da biblioteca, enquanto a configuração de implementação deve ser usada para declarar dependências internas ao componente.

Para mais explicações, consulte esta imagem. Breve explicação


46

Breve solução:

A melhor abordagem é substituir todas as compiledependências por implementationdependências. E somente onde você vazar a interface de um módulo, você deve usar api. Isso deve causar muito menos recompilação.

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

Explique mais:

Antes do plugin Android Gradle 3.0 : tivemos um grande problema: uma alteração de código faz com que todos os módulos sejam recompilados. A causa raiz disso é que Gradle não sabe se você vazou a interface de um módulo através de outro ou não.

Após o Android Gradle plugin 3.0 : o mais recente plugin do Android Gradle agora exige que você defina explicitamente se você vaza a interface de um módulo. Com base nisso, ele pode fazer a escolha certa sobre o que deve recompilar.

Como tal, a compiledependência foi descontinuada e substituída por duas novas:

  • api: você vaza a interface deste módulo através de sua própria interface, o que significa exatamente o mesmo que a compiledependência antiga

  • implementation: você só usa este módulo internamente e não o vaza pela interface

Portanto, agora você pode dizer explicitamente à Gradle para recompilar um módulo se a interface de um módulo usado mudar ou não.

Cortesia do blog Jeroen Mols


2
Explanação limpa e concisa. Obrigado!
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.  |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+

Não responder à pergunta diretamente
skryvets

1
Há também um developmentOnly
Hohenheimsenberg

O que devo usar se precisar de tempo de execução e tempo de compilação? Atualmente, eu tenho implementationseguido por um runtime.
Maroun 28/03

8

A breve diferença no termo do leigo é:

  • Se você estiver trabalhando em uma interface ou módulo que forneça suporte a outros módulos, expondo os membros da dependência declarada, você deverá usar 'api'.
  • Se você estiver criando um aplicativo ou módulo que implementará ou usará a dependência declarada internamente, use 'implementação'.
  • 'compilar' funcionou da mesma forma que 'api'; no entanto, se você estiver implementando ou usando apenas uma biblioteca, 'implementação' funcionará melhor e economizará recursos.

leia a resposta de @aldok para um exemplo abrangente.


Mas o fato é que, se uma pessoa veio aqui deliberadamente, procurando a resposta para essas perguntas, então ele não é um leigo, afinal.
Rishav

6

Desde a versão 5.6.3, a documentação do Gradle fornece regras simples para identificar se uma compiledependência antiga (ou nova) deve ser substituída por uma implementationou uma apidependência:

  • Prefira a implementationconfiguração apiquando possível

Isso mantém as dependências fora do caminho de classe de compilação do consumidor. Além disso, os consumidores falharão imediatamente na compilação se algum tipo de implementação vazar acidentalmente na API pública.

Então, quando você deve usar a apiconfiguração? Uma dependência de API é aquela que contém pelo menos um tipo exposto na interface binária da biblioteca, geralmente chamada de ABI (Application Binary Interface). Isso inclui, mas não se limita a:

  • tipos usados ​​em super classes ou interfaces
  • tipos usados ​​em parâmetros de métodos públicos, incluindo tipos genéricos de parâmetros (onde público é algo visível aos compiladores. Ou seja, public, protected e empacota membros privados no mundo Java)
  • tipos usados ​​em campos públicos
  • tipos de anotação pública

Por outro lado, qualquer tipo usado na lista a seguir é irrelevante para a ABI e, portanto, deve ser declarado como uma implementationdependência:

  • tipos utilizados exclusivamente em corpos de métodos
  • tipos usados ​​exclusivamente em membros privados
  • tipos encontrados exclusivamente em classes internas (versões futuras do Gradle permitem declarar quais pacotes pertencem à API pública)

6

Gradle 3.0 introduziu as próximas mudanças:

  • compile -> api

    api palavra-chave é igual a obsoleta compile

  • compile -> implementation

    É preferível , pois tem algumas vantagens. implementationexpor a dependência apenas para um nível acima no tempo de construção (a dependência está disponível no tempo de execução). Como resultado, você tem uma construção mais rápida (não é necessário recompilar os consumidores com mais de 1 nível)

  • provided -> compileOnly

    Essa dependência está disponível apenas em tempo de compilação (a dependência não está disponível no tempo de execução). Essa dependência não pode ser transitiva e ser .aar. Ele pode ser usado com o processador de anotação em tempo de compilação e permite reduzir um arquivo de saída final

  • compile -> annotationProcessor

    Muito semelhante, compileOnlymas também garante que a dependência transitiva não é visível para o consumidor

  • apk -> runtimeOnly

    A dependência não está disponível em tempo de compilação, mas disponível em tempo de execução.


Portanto, em outras palavras, api = public, implementation = internale compileOnly = private- eu preciso para criar esses aliases para estas funções como eles são super confuso.
t3chb0t
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.