Flavors!

English version here.

O mesmo produto, vários sabores.

Um mecanismo poderoso no Android é a capacidade de gerar múltiplas versões customizadas de uma aplicação, usando o mesmo projeto. Um mesmo aplicativo pode ter diferentes versões onde a mudança entre eles pode ser visual (ícones, cores, textos…) e/ou por funcionalidades.
 Este mecanismo é chamado de Product Flavors (a grosso modo, sabores do produto em inglês), onde a ideia é que você tenha vários “sabores” do mesmo aplicativo.

Esta configuração é possível graças ao Android Studio e o plugin do Gradle.


Onde posso utilizar?

¯\_(ツ)_/¯

Um exemplo clássico de uso do flavors é quando o aplicativo tem a versão paga e a versão grátis, com menos funcionalidades. Ou quando precisa lançar uma versão experimental e posteriormente a versão oficial. Ou imagine que o cliente pediu um aplicativo idêntico (mas diferente) e gostaria que mudasse apenas a cor do app, o ícone e tirasse alguns fluxos e adicionasse algumas funcionalidades. Utilizando flavors você não precisaria lidar com código duplicado, múltiplos versionamentos, possíveis erros humanos na hora de copiar códigos e resources, testar duas vezes… basta adicionar a configuração para isso acontecer.


Falar é fácil, me mostre código

Me mostre o código e pare de falar!
A regra mandatória que devemos ter em mente é que a diferenciação de um aplicativo de outro é o seu package, também conhecido como applicationId. Este package não são os pacotes java da sua aplicação e sim a assinatura do seu aplicativo. 
É por ele que o Google identifica o aplicativo no Google Play e o Android no aparelho. Portanto, se houver dois (ou mais) aplicativos onde a diferença entre eles é mínima deverão ter package diferente.

No exemplo será feito 3 aplicativos a partir de um único projeto. Chamaremos um aplicativo de Regular, que corresponderá a um versão completa do aplicativo. Outro de Demo, que corresponderá à uma versão grátis do aplicativo, com menos recursos. E o terceiro aplicativo de Winter que corresponderá à uma versão igual a completa, porém customizada visualmente.

A primeira coisa que devemos configurar são os packages no build.gradle do seu aplicativo (aquele da pasta app/). Utilizando a tag productFlavors você configura quais flavors existirá.

Configurando os Flavors do Projeto no app/build.gradle

No flavor regular não é definido nada para que ele use tudo que o defaultConfig tem configurado. Entretanto, é necessário deixar o seu nome como um flavor para o gradle entender que existirá esta aplicação. Caso contrário ele irá criar apenas os flavors abaixo.

Em demo é adicionado um sufixo para o applicationId com a tag applicationIdSuffix. Com isso a assinatura deste aplicativo será com.orafaaraujo.sweetdroid.demo, o diferenciando do aplicativo regular que será apenas com.orafaaraujo.sweetdroid.

E em winter é adicionado a tag applicationId, sendo assim substituindo a assinatura em defaultConfig e criando uma nova, no caso com.orafaaraujo.wintersweetdroid. Além disso, também é configurado um novo versionCode e um novo versionName, chaves usada para controle de versões de Google Play.

Feito isso, espere o gradle sincronizar…

Porque demoras tanto, gradle? :(

No Android Studio aparecerá a tab Build Variants (geralmente esta tab fica no canto inferior esquerdo com o ícone do Android) a opção de trocar as variantes do seu projeto, de acordo com cada flavor. Basta seleciona qual deseja, esperar o Android Studio sincronizar e rodar sua app em Debug. Nota-se que também é possível instalar os 3 aplicativos no mesmo aparelho, justamente pela diferença no applicationId.

Diferentes tipos de build.
Por que aparece 6 tipos se foi criado apenas 3 flavors?
Porque cada build tem a versão de Debug voltada para o tempo desenvolvimento e a versão de Release que é a versão final que será enviada para o Google Play para os usuários baixarem. Em nosso caso, é recomendável utilizar sempre a versão de Debug.

Personalização dos aplicativos

Como dito anteriormente, é possível personalizar tanto a parte visual (telas, ícones, imagens e etc) e a parte de funcionalidades de cada aplicativo (arquivos Java). 
Para personalizar cada flavor é necessário criar uma pasta dentro do source do modulo (precisamente em app/src/ se o seu modulo for app) correspondente ao nome de cada flavor. No projeto de exemplo será personalizar cada flavor, portanto será criado uma pasta para cada flavor, ficando como na figura abaixo.

Cada pasta corresponde a um flavor criado no build.gradle

Nesta pasta já existe a pasta main, onde dentro está a pasta java com as classes, res onde estão seus resources e o seu manifesto. O que se deve fazer para personalizar um flavor é criar um arquivo com o mesmo nome e caminho do que existe na pasta main. Ou, caso cada flavor tenha seu arquivo diferente, basta criá-lo em cada pasta e não será necessário ter o da pasta main.


Por dentro do App

Hmmmmm ❤

Layout, imagens e xml

A principio veremos como foi construída a tela inicial de cada flavor. A tela é a mesma para todos os flavors, o que irá mudar é apenas o nome do app na Toolbar, as cores do app e a imagem. Dito isso, vamos aos arquivos.

main/res/layout/activity_main.xml
main/res/values/strings.xml

O layout activity_main.xml e a string main_text ficarão dentro das pasta main, pois será igual para todos flavors.

O nome do app que é configurado no AndroidManifest pelo arquivo string.xml de cada flavor.

regular/res/values/strings.xml
demo/res/values/strings.xml
winter/res/values/strings.xml
por ora, ignore a string second_text :-)

A imagem referenciada no layout deverá respeitar a lógica dos títulos dos aplicativos também.

(regular, demo, winter)/res/drawable/main_image.png

E para finalizar, vamos às cores. 
Já que as apenas o aplicativo Winter terá cores diferentes, sua configuração ficará em um arquivo separado, dentro da pasta do seu flavor. As cores do aplicativo Regular e Demo serão iguais, portanto sua configuração de cores ficará dentro da pasta main

main/res/values/colors.xml
winter/res/values/colors.xml

Com isso, com poucas mudanças temos temos três apps diferentes! 
Esse resultado na tela inicial de cada aplicativo.

Regular, Demo e Winter

Ótimo, e os ícones de inicialização? Como diferenciar? Criando arquivos em cada pasta de cada flavor! 
E como tem notado, essa regra se aplicará para qualquer configuração customizada de cada aplicativo. Inclusive código Java. Vamos à ele exibindo a segunda tela.


Código Java

Em cada tela existe um botão chamado Mais, ele nos levará para nossa segunda tela. 
Para exemplificar que também é possível haver classes Java diferentes para cada flavor foi criado uma segunda tela com código diferente.

Primeiro vamos chamar a próxima tela.

main/java/com/orafaaraujo/sweetdroid/MainActivity.java

Como é apenas um exemplo, iremos criar 3 arquivos SecondActivity.java, um para cada flavor, porém serão todos iguais. A única diferença neste caso é que todos estarão referenciando o mesmo xml, que está em main/res/layout/activity_second.xml.

(Regular, demo e Winter)/java/com/orafaaraujo/sweetdroid/SecondActivity.java
main/res/layout/activity_second.xml

Como o arquivo activity_second.xml ficará na pasta main ele será o mesmo para todos flavors. A única coisa diferente entre eles será o texto, que virá dos arquivos string.xml de cada um, já exibidos acima.

Pronto, é isso!
Utilizando pouco código temos 3 aplicativos diferentes.

#winning

Android Studio

Caso esteja utilizando a visão Android da aba Project, o Android Studio exibe de maneira eficiente quais arquivos pertencem ao flavor atualmente selecionado colocando o seu nome entre parenteses, diferenciando dos arquivos comuns de todos flavors, no caso, os que permanecem na pasta main.

Visão Android

Caso esteja usando a visão Project sua hierarquia de arquivos ficará desta maneira, exibindo cada pasta dentro de cada flavor.

Visão Project

BÔNUS

Build Types

Sabores e versões diferentes

Outra configuração muito útil do Android Studio/plugin do Gradle se chama buildTypes (tipos de build, em inglês).
Com ela é possível gerar a mesma aplicação para ambientes diferentes, por exemplo, uma para desenvolvimento, uma para testes/homologação/qualidade e outra para produção.


Onde posso utilizar?

Mas tipo… a gente já não… isso já não…

É muito comum ter ambientes diferentes do mesmo aplicativo. Segue alguns exemplos:

  • O ambiente de desenvolvimento é “esperado” que acontecerá mais erros pois o aplicativo ainda está em tempo de criação. O desenvolvedor também precisará trabalhar com dados fictícios parar poder criar, editar ou apagar dados que não irão afetar dados reais de usuários.
  • Um ambiente de testes/homologação/qualidade tem um cenário parecido com o de desenvolvimento na questão de dados fictícios, porém, espera-se que haja menos erros… e qualquer erro deverá ser reportado para a equipe de desenvolvimento com o maior quantidade de informação util possível.
  • Já um ambiente de produção será onde haverá dados de usuários reais, com sistemas para métricas como Analytics, mecanismos para capturar erros de usuário e etc. Além do mais, para subir um aplicativo para o Google Play é necessário que o aplicativo esteja assinado e não esteja debugável. Outra configuração que só faz sentido em aplicativos em produção é o Proguard habilitado.

Onde a magia acontece

Para configurar vamos de novo ao arquivo app/build.gradle. 
Agora é criado dentro da tag buildTypes as tipos de build que queremos.

app/build.gradle

Abaixo está detalhado cada configuração usada no exemplo:

  • debuggable: Como dito anteriormente, as versões de desenvolvimento e homologação são bem parecidas. Ambas são debugáveis com a tag debuggable true. Já na versão de produção mudamos para debuggable false, pois como já foi dito não é possível fazer upload de um apk para o GooglePlay caso ele esteja debugável.
  • applicationIdSuffix e versionNameSuffix: Novamente configuramos mudanças no applicationID com a tag applicationIdSuffix, para que seja possível instalar as 3 versões do aplicativo no mesmo aparelho. Além disso, alteramos também o versionNameSuffix, para diferenciar o nome da versão do aplicativo.
  • signingConfig: Na versão de QA e na versão de release colocamos a configuração de assinatura de aplicativo com a tag signingConfig signingconfigs.release, para que seja possível criar um apk assinado para submeter ao Google Play ou se o seu aplicativo utiliza compras/assinaturas dentro do app através de pagamento via GooglePlay (é necessário um app assinado até mesmo para testar compras/assinatura).
  • minifyEnable: Na versão de produção também habilitamos o ProGuard. Caso não saiba do que isso se trata você pode ler sobre diretamente na documentação do Android aqui ou neste post do Android Dev Br.
  • BuildConfigField: E por último mas não menos importante, com a tag BuildConfigField podemos criar variáveis que estarão no objeto BuildConfig gerado pelo gradle. Em nosso exemplo, criamos as mesmas variáveis em cada build mas cada uma com valor diferente. 
    Com a tag declarada BuildConfigField “String”, “ENDPOINT”, “http…” definimos que uma variável chamada ENDPOINT do tipo String estará no objeto BuildConfig com o valor associado ao seu BuildType.
    Outra configuração usada em nosso projeto de exemplo é a BuildConfigField “boolean”, “REPORT_CRASH”, “true/false” para podermos habilitar o nosso reportador de erros apenas quando os erros vierem de usuários. Caso você deixe essa opção habilitada em tempo de desenvolvimento você terá muitos registros/e-mails de erros que você acabou de encontrar no desenvolvimento. Evite esse aborrecimento para você e para o seu time. :-)
Versão de debug? Não precisa me enviar o erros, obrigado :-)

No exemplo a variável REPORT_CRASH é verificada dentro da MainActivity, que é a primeira classe a ser chamada. Caso o projeto tenha uma classe que herde Application é uma boa prática colocar esta verificação neste ponto, já que é uma classe que sempre será chamada, independente de como a Aplicação será iniciada.

Vale lembrar que a utilização das variáveis String ENDPOINT e boolean REPORT_CRASH servem apenas para exemplificar é possível configurar qualquer tipo de dado para cada build, seja texto, número ou booleano.

Considerações finais

Código de exemplo

O código utilizado no post encontra-se neste repositório do GitHub.

Documentação oficial

Na documentação do Android há uma parte falando sobre Build Variants, você encontrará neste link.

Contatos

Podem me encontrar pelo e-mail orafaaraujo@gmail.com, pelo skype orafaaraujo, e pelo slack/medium/twitter @orafaaraujo. 
Feedbacks são bem-vindos!

Comunidade Android

Participe do maior forum sobre Android no Brasil no slack Android Dev BR
Site: http://www.androiddevbr.org | Convite: http://slack.androiddevbr.org

Obrigado!