Concrete Logo
Hamburger button

Como fazer Integração Contínua no Android – parte 3

  • Blog
  • 2 de Abril de 2014
Share

Pois bem, já temos uma ideia dos sistemas de build do Android e sabemos montar nosso ambiente de desenvolvimento. Mas não sabemos como começar o projeto (ou migrar um projeto existente) com tudo o que fizemos até agora. Well, well, well… que não seja por isso!

Não pretendemos explicar o MAVEN inteiro aqui. Acreditem, um sistema de build pode ficar cada vez mais e mais complexo. Só para gerenciar as dependências já seria algo bastante grande… dependências reflexivas, versões diferentes em bibliotecas diferentes e etc. Por exemplo, esse tema está em alta na comunidade Fedora com o desenvolvimento do DNF (próximo gerenciador de pacotes da distribuição).

Mas não desesperem! Para nós as coisas serão mais simples que isso…

Todo projeto MAVEN precisa de um arquivo descritor chamado sempre de pom.xml. Este arquivo XML conterá minimamente para um projeto Android:

  • Grupo (ex: br.com.concretesolutions), Id do artefato (ex: app.de.exemplo) e versão (ex: 3.0.1);
  • Propriedades gerais (ex: encoding dos arquivos e versões das dependências);
  • Dependências (ex: com.google.android);
  • Definição do build (ex: configuração do plugin de android);

Um exemplo mínimo de pom.xml seria:

[code language=”xml”]
<project xmlns="https://maven.apache.org/POM/4.0.0"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<!– definições do projeto –>
<groupId>br.com.concretesolutions</groupId>
<artifactId>exemplo-ic</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>apk</packaging>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<api.platform>18</api.platform>

<android.version>4.1.1.4</android.version>
<android-maven-plugin.version>3.8.1</android-maven-plugin.version>
</properties>

<dependencies>
<dependency>
<groupId>com.google.android</groupId>
<artifactId>android</artifactId>
<version>${android.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.android</groupId>
<artifactId>android-test</artifactId>
<version>${android.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
<finalName>exemplo-ic</finalName>
<sourceDirectory>src</sourceDirectory>
<outputDirectory>bin/classes</outputDirectory>
<plugins>
<plugin>
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
<artifactId>android-maven-plugin</artifactId>
<version>${android-maven-plugin.version}</version>
<extensions>true</extensions>
<configuration>
<sdk>
<platform>${api.platform}</platform>
</sdk>
</configuration>
</plugin>
</plugins>

<!– Evita erros no Eclipse! –>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
<artifactId>android-maven-plugin</artifactId>
<versionRange>${android-maven-plugin.version}</versionRange>
<goals>
<goal>consume-aar</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore />
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
[/code]

Quase tudo é bem auto-explicativo. Na ordem temos:

  • A declaração do namespace e sua versão (modelVersion=4.0.0)
  • As definições de identificação do nosso projeto para o Maven de grupo, artefato, versão e etc.
  • As propriedades do projeto como versão da API Android, dependências e etc.
  • As dependências mínimas (android, android-test agora)
  • Configurações de build. Aqui está o truque! Vamos detalhar bastante o plugin android-maven-plugin logo abaixo
  • Uma configuração para o Eclipse (não precisa em outras IDEs)

Como já foi mencionado antes, o intuito não é se aprofundar no Maven aqui, mas sim naquilo que possibilita usar o Maven para o Android, ou seja, o plugin de build android-maven-plugin.

Neste momento temos um projeto Android normal (sem alterações) MAIS um arquivo pom.xml. Ele deve estar parecido com isso:

Estrutura de projeto Android + Maven

Neste momento já conseguimos compilar e rodar o projeto com o seguinte comando:

[code language=”shell”]
mvn clean install
[/code]

Seu console irá pirar com diversos logs, seu computador irá provavelmente baixar a internet inteira (brincadeira de mal gosto que muitos fazem com o Maven :D) e no final deverá mostrar algo como:

[code]
[INFO] Installing /home/victor/dev/android/workspace/exemplo-android-ic/target/exemplo-ic.jar to /home/victor/.m2/repository/br/com/concretesolutions/exemplo-ic/0.0.1-SNAPSHOT/exemplo-ic-0.0.1-SNAPSHOT.jar
[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time: 5.737s
[INFO] Finished at: Wed Apr 02 15:20:35 BRT 2014
[INFO] Final Memory: 21M/135M
[INFO] ————————————————————————
[/code]

O que aconteceu? Bom, agora o maven criou uma pasta target com tudo que foi gerado pelo Maven. Lá temos o apk, o classes.dex, a pasta res entre outras coisas. Depois da execução, se você atualizar o projeto (F5 no projeto), verá uma estrutura aproximadamente assim:

Estrutura do proejto Android + Maven após compilação

Esse apk gerado ainda não está assinado para publicação no Google Play. Veja como assinar na documentação do plugin, mas isso é feito com o maven jar signer.

O plugin nos provê diversos goals para automatizar testes, deploys execução e etc. Normalmente durante o desenvolvimento você irá querer executar:

[code language=”shell”]
mvn clean install android:deploy android:run
[/code]

Isso irá instalar o aplicativo no aparelho ou emulador e executará a aplicação. Para isso usamos dois goals do plugin, o deploy e o run. Para não termos que nos preocupar com a aplicação já estar instalada no alvo, usamos a propriedade de configuração undployBeforeDeploy:

[code language=”xml”]

<plugin>
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
<artifactId>android-maven-plugin</artifactId>
<version>${android-maven-plugin.version}</version>
<extensions>true</extensions>
<configuration>
<undeployBeforeDeploy>true</undeployBeforeDeploy>
<sdk>
<platform>${api.platform}</platform>
</sdk>
</configuration>
</plugin>

[/code]

Assim temos uma aplicação que podemos incluir dependências diretamente do Maven Central Repository SEM TER QUE ADICIONAR JARs NA MÃO NA PASTA LIBs!!! É importante lembrar que as dependências em libs (ou lib dependendo da sua versão do plugin ADT) não estarão no classpath do Maven, ok?

Mas e como eu faço para usar uma dependência que não está no Maven Central??? Este é o grande problema do Android… muitos serviços de analytics, advertisement, reporting, monitoring e etc são bibliotecas privadas que não estarão no Maven Central. Então esquece do Maven?

Jóia!

Mas claro que não teríamos chegado até aqui para desistir dele…

Ok do Chuck Norris!

Existem algumas abordagens para este problema: criar um diretório dentro do aplicativo que siga uma estrutura de pastas de um repositório Maven e referenciar essa configuração do pom, ou instalar cada uma das dependências fechadas no repositório Maven local. Entre as duas, a segunda talvez exija mais um passo de setup inicial do projeto. Já a primeira é mais portável, pois o Maven vai encontrar as dependências tendo como base o próprio projeto. Essa segunda abordagem é a que o próprio Google escolheu. Como ele não podia colocar os artefatos no Maven Central por questão de licença, ele criou um repositório à parte que você aceita a licença antes de poder baixá-lo. Isso está disponível no SDK Manager do Android na parte de extras. Existe um repositório Maven para os artefatos Android e outro para os artefatos Google mesmo como Play Services e etc.

Android SDK Manager

Reparem no Android support repository e no Google repository. Ao baixá-los, teremos a pasta de extras dentro de $ANDROID_HOME. Dentro dessa pasta há o android/m2repository. Para configurarmos esses repositórios no nosso projeto teremos que adicionar algumas configurações no pom.xml:

[code language=”xml”]

<repositories>
<repository>
<id>android-repo</id>
<url>file://${ANDROID_HOME}/extras/android/m2repository</url>
</repository>
<repository>
<id>android-libs</id>
<url>file://${project.basedir}/private_libs</url>
</repository>
</repositories>

[/code]

Agora temos o repositório do Google configurado e um outro que aponta para a pasta private_libs logo na raiz do nosso projeto. Para incluírmos dependências lá basta seguirmos um layout exemplificado a seguir:

Dependência privada no projeto Android Maven

À esquerda temos o layout de pastas do repositório private_libs. À direita temos a declaração da dependência no pom do projeto.

Quase pronto! Falta apenas atualizarmos nosso repositório. Para isso precisamos rodar o comando:

[code language=”shell”]
mvn clean install -U
[/code]

Este parâmetro “-U” força o Maven a atualizar as dependências o que o forçará a buscar nos novos repositórios.

Pronto! Temos um projeto Android Maven com dependências do Google, mais um repositório local para adicionarmos as dependências privadas que encontrarmos!

No próximo passo falaremos de como configurar os testes! E depois como executar a cobertura! E depois como botar tudo isso no Jenkins! Ufa… é um processo longo, mas não tão complexo. E os frutos desse trabalho serão paz e tranquilidade nos finais de semana… Afinal de contas, nossos releases estarão testados de ponta a ponta a cada commit…

Dúvidas até aqui? Deixe nos comentários!