Android

11 fev, 2014

Gradle??? É de comer ou passar no cabelo?

Publicidade

No último Google I/O foi anunciado o Android Studio, uma nova IDE de desenvolvimento para Android.

Essa nova IDE, baseada na IntelliJ IDEA da JetBrains, tem como uma de suas principais features, e por muitas vezes ignorada, um novo sistema de build muito mais moderno baseado em gradle.

Afinal, o que é gradle?

Gradle é um sistema de automatização de builds, assim como o Ant e Maven, e pode ser assim definido:

A combinação do poder e flexibilidade do Ant com o gerenciamento de dependências e convenções do Maven em uma maneira mais eficaz. (tradução livre do site)

Como o objetivo deste artigo não é falar do gradle em si, e sim sobre o seu uso com Android, sugiro algumas leituras complementares, como o site oficial do gradle e este artigo explicando algumas vantagens dele sobre o Ant e Maven.

Começando do começo

Nesta primeira parte de uma série de artigos, irei explicar os conceitos básicos do novo sistema de build.

Primeiro, precisamos ter um entendimento básico do gradle, e de como ele funciona em um projeto Android, por isso iremos utilizar projeto de exemplo para explicar alguns pontos chave.

Iniciamos criando um novo projeto de exemplo no Android Studio.

android-1

A primeira diferença que notamos é a estrutura de diretórios, que é diferente do que estamos acostumados no Eclipse.

android-2

Um projeto no Android Studio é normalmente composto de um ou mais módulos. No nosso caso, temos um módulo gradle-test que é nosso aplicativo exemplo.

Nosso diretório raiz, GradleTest, é onde ficam todas as configurações do projeto, como o build.gradle, que é a configuração de build global, e o settings.gradle, onde definimos quais módulos iremos ‘buildar’.

Já o código do aplicativo fica em uma estrutura:
gradle-test/src/main/java -> Código Java
gradle-test/src/main/res -> Recursos
gradle-test/src/main/AndroidManifest.xml -> Manifesto

Essa estrutura é importante para depois podermos trabalhar com buildTypes e flavors, mas isso são assuntos para próximos artigos.

Sujando as mãos…

Após a criação do projeto, nosso GradleTest/builg.gradle global será algo mais ou menos como isso (notem que eu marquei alguns pontos com [número] para explicar abaixo).

buildscript { [1]  
    repositories { [2]
        mavenCentral()
    }
    dependencies { [3]
        classpath 'com.android.tools.build:gradle:0.8.+'
    }
}

allprojects { [4]  
    repositories { [5]
        mavenCentral()
    }
}

[1] buildscript é um bloco de configurações que dizem respeito ao próprio build.
[2] repositories é a configuração de repositórios externos para download de dependências.
[3] dependencies é a lista de dependências ou plugins utilizados para realizar esse build. No nosso caso, estamos instruindo o gradle a ter como dependência o plugin do android (leia sobre plugins).
[4] allprojects configurações pertinentes a todos módulos do projeto.
[5] repositories repositórios de dependências disponíveis para todos os módulos.

Já o GradleTest/settings.gradle indica quais módulos pertencem ao projeto.

include ':gradle-test'

Nesse caso, temos apenas nosso app no subdiretório gradle-test, mas poderíamos ter outros módulos implementando uma library, ou mesmo um segundo app.

Deixando de lado as configurações globais, o build de nosso app é configurado por GradleTest/gradle-test/build.gradle.

apply plugin: 'android' [1]

android { [2]  
    compileSdkVersion 19
    buildToolsVersion "19.0.1"

    defaultConfig { [3]
        minSdkVersion 14
        targetSdkVersion 19
        versionCode 1
        versionName "1.0"
    }
}

dependencies { [4]  
}

[1] apply plugin instrui o gradle a aplicar um plugin; nesse caso, ‘android’ (se estivéssemos trabalhando com um projeto de lib seria ‘android-library’.
[2] android esse é o bloco de configurações específicas do Android, como qual versão do SDK será usada na compilação, qual versão das ferramentas de build etc.
[3] defaultConfig contém algumas configurações customizáveis, essas informações farão parte do AndroidManifest.xml final, gerado pelo build. Aqui que começa a mágica do gradle, pois podemos começar a controlar atributos automaticamente no build de maneira bem simples, como por exemplo autoincrementar o versionCode, ou gerar o versionName a partir de informações do SCM etc. Veremos mais sobre essas configurações durante esta série sobre gradle.
[4] dependencies lista quais as dependências do aplicativo

Configurando dependências

Configurar dependências sem precisar importar arquivos .jar ou bibliotecas em código para o projeto é um dos grandes ganhos de se usar gradle no desenvolvimento Android.

No Eclipse, se você não utilizar maven, para importar a biblioteca de compatibilidade, você terá que copiar manualmente o arquivo android-support-v4.jar para o diretório libs/, e gerenciar as versões futuras da biblioteca manualmente, ou seja, a cada nova versão, você tem que novamente copiar o novo .jar.

Com gradle, o processo fica bem mais simples: basta apenas adicionar uma linha ao bloco de dependências.

dependencies {  
    compile 'com.android.support:support-v4:+'
}

Com essa linha, instruímos o gradle a adicionar a biblioteca de compatibilidade na sua última versão (atualmente 19.0.1, mas caso o Google venha a lançar a versão 19.0.2 ou 20.0.0, o gradle automaticamente utilizará a nova versão).

Ainda não se convenceu?

Ok, adicionar um .jar ao projeto não é realmente muito trabalhoso, mas e adicionar e manter suporte para app-compat ou para play-services, ou para ActionBarSherlock?

Bibliotecas .jar não podem conter resources como imagens (drawables), layouts, strings etc., então temos que importar o código diretamente no workspace do Eclipse para poder utilizar essas bibliotecas.
E se uma nova versão é disponibilizada, como fazemos? Temos que atualizar o código importado também.

Bom, o novo sistema de build suporta um novo formato de distribuição de bibliotecas que contenham resources, chamado AAR. Através desse formato, é possível importar uma biblioteca aar com a mesma facilidade que uma .jar.

dependencies {  
    compile "com.android.support:appcompat-v7:+"
    compile 'com.google.android.gms:play-services:+'
}

Apenas isso e o gradle se encarrega de baixar as dependências, sem precisar importar bibliotecas para dentro de nosso workspace.

Cenas do próximo capítulo

Na próxima parte desta série, falarei sobre Build Types, ou como ter diferentes configurações, como URLs, para suportar ambientes de desenvolvimento e produção (e outros).