Layouts e Resources – Tutorial Android 4

Padrão

Olá pessoal! No último post criamos a nossa primeira Activity, e entendemos um pouco sobre Intents! No post de hoje vamos entender um pouco mais sobre resources no Android e criar um layout para a nossa Activity. Preparados?

Para o tutorial de hoje, vamos trabalhar majoritariamente com a pasta res, que fica localizada no caminho app/src/main/res. É nela que ficam todos os recursos do seu projeto, que não são necessariamente código Java. O projeto, no momento, deve ter algumas pastas, como drawable (vazia), mipmap (em algumas versões com sufixos estranhos) e values.

Um projeto Android padrão costuma ter várias outras, que entraremos em mais detalhes a medida que forem necessárias. Essas pastas separam os tipos de recursos que temos em nosso projeto. Por exemplo, drawable, conterá qualquer tipo de coisa desenhável, desde formas, a imagens e seletores. A mipmap contém somente o ícone do aplicativo. A pasta values contém valores em gerais, desde inteiros, até strings, cores, dimensões, estilos e etc.

Se você abrir cada uma das pastas (mipmap-hdpimipmap-mdpimipmap-xhdpi, etc), você verá que temos o ícone do aplicativo em diferentes tamanhos. Isso ocorre porque os dispositivos Android possuem diversas configurações. Configuração diz respeito a tudo no dispositivo, desde idioma, até o que chamamos de densidade da tela. Uma maior densidade, quer dizer que aquele dispositivo tem mais pixels em uma menor área física. Exemplo: um dispositivo que tem uma tela de 4,7″ e uma resolução de 480×800 pixels tem uma densidade mais baixa (hdpi) do que um device que tem a mesma tela de 4,7″, porém uma resolução de 1080×1920 (xxhdpi). Exatamente por suportar uma diversidade grande de tamanhos de tela, é que no Android não trabalhamos com pixels, e sim com density pixels. Assim, um botão tende a ter sempre “mais ou menos o mesmo tamanho”, independente da resolução da tela. Para aprender mais sobre isso, dê uma olhada na documentação do Android (que agora também possui versão em português!).

E como essa mágica é feita? Não é mágica. Ao colocar um qualificador na pasta, o Android, na hora que estiver executando, conhece a configuração do dispositivo e usa o recurso específico para a sua configuração. Assim, não precisamos fazer várias condições em nosso código. É importante lembrar que podemos ter vários tipos de qualificadores ou mesmo adicionar pastas com mais de um qualificador. Veremos alguns casos em outros posts 😉

Assim, resumindo, Supondo que estou em dispositivo xhdpi e preciso do resource mipmap/ic_launcher, o Android automaticamente buscará em mipmap-xhdpi/ic_launcher. Caso não exista uma versão específica para a configuração, ele busca a opção mais genérica, sem nenhum sufixo.

Bom, agora que já estamos mais familiarizados com o mecanismo de resources e pastas no Android, vamos criar uma nova pasta dentro de res, chamada layout. Essa pasta, como o próprio nome sugere, guardará arquivos que representam os layouts das nossas telas. Vamos criar um arquivo chamado activity_main.xml. A grande maioria dos resources no Android utilizam a notação de XML.

Neste arquivo, primeiramente colocaremos um container, o LinearLayout. Os containers servem para organizar os elementos na tela. No caso do LinearLayout, ele organiza os elementos alinhados, um após o outro, utilizando a orientação vertical ou horizontal. Adicionando o LinearLayout, o arquivo activity_main.xml fica assim:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

</LinearLayout>

Você pode perceber que, além da orientação, também configuramos a largura (width) e a altura (height) do elemento. Para essas dimensões, podemos utilizar um valor ou as constantes match_parentwrap_contentmatch_parent indica que o elemento adotará para essa dimensão, a mesma dimensão que seu elemento pai. No caso, como o LinearLayout é o primeiro elemento da nossa hierarquia de views, ele adotará o tamanho de seu pai (algum container do Android que representa a tela). Assim, temos um LinearLayout do tamanho da tela 🙂

Vamos agora adicionar dois campos de texto e um botão ao nosso layout.

Os campos de entrada de texto no Android são conhecidos como EditText. Além da altura e da largura, também especificamos o id e a propriedade hint. Ids no Android são identificadores, o que nos permitirá referenciar este elemento em outros lugares (seja no XML, seja nas nossas classes Java). A notação @+id indica que estamos criando este recurso caso ele não exista. Sempre que você ver a notação @ em um XML, significa que estamos referenciando um recurso. O + indica a criação e id indica que é um resource do tipo id. Já o hint indica um texto de sugestão que será exibido quando o campo estiver vazio, indicando ao usuário o que deve ser inserido. Também estamos utilizando a notação de recurso, dessa vez referenciando um recurso do tipo string (que terá um valor correspondente dentro de res/values/strings.xml):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_vertical"
    android:orientation="vertical">

    <EditText
        android:id="@+id/name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/name" />

    <EditText
        android:id="@+id/address"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/address" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/save" />

</LinearLayout>
<!-- Arquivo strings.xml -->
<resources>
    <string name="app_name">Tutorial</string>
    <string name="save">Save</string>
    <string name="address">Address</string>
    <string name="name">Name</string>
</resources>

No caso do Button, temos atributos semelhantes, com a diferença de que utilizamos a propriedade text em vez de hint. Com a propriedade text, teremos um texto no botão.

Com o arquivo de layout criado, vamos voltar a nossa classe MainActivity e definir este arquivo de layout, vinculando-o a Activity. Para isso, vamos utilizar ainda o método onCreate() que é chamado sempre que a Activity é criada. Nele, vincularemos o nosso layout XML a Activity. Fazemos isso chamando o método setContentView(). Este método pode receber um objeto do tipo View ou uma referência a um resource de layout. Note o uso da classe R. Esta classe é gerada automaticamente a cada build do projeto, e é uma referência a todos os resources que temos no projeto, para uso no nosso código Java. Vamos também, remover os Logs que adicionamos anteriormente. O método fica assim:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

Com esse XML, caso execute o aplicativo, nossa tela fica com o seguinte layout:

Para uma experiência mais agradável, poderíamos centralizar os elementos verticalmente. Para isso, podemos adicionar a propriedade gravity com o valor center_vertical. Essa propriedade indica como os filhos do container vão se comportar, conforme forem adicionados. Assim, temos a tela final:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_vertical"
    android:orientation="vertical">

    <EditText
        android:id="@+id/name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/name" />

    <EditText
        android:id="@+id/address"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/address" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/save" />

</LinearLayout>

E é isso pessoal! No próximo post veremos o mecanismo de binding de views e callbacks no Android! Caso deseje baixar o projeto que desenvolvemos até agora, você pode encontrar no Github.

Até o próximo post!

Entendendo a Activities e Intents – Tutorial Android 3

Padrão

Olá pessoal! Dando prosseguimento a nossa série de tutoriais, hoje vamos criar a nossa primeira Activity, entender o que ela é e como funciona, além de uma noção geral sobre Intents! Vamos lá?

No último post criamos um projeto no famoso estilo Hello World. Para este, vamos criar um projeto que será evoluído ao longo de toda a série. Para isso, crie um novo projeto seguindo o tutorial anterior, com a diferença de que, em vez de selecionar Empty Activity, selecione a opção Add No Activity. Assim, podemos controlar melhor o que é criado e entender o que está acontecendo.

Algumas configurações iniciais serão realizadas e um modelo básico de projeto será criado. A princípio não se preocupe, vamos focar nos pontos um a um ao longo desta série. Assim que o projeto é criado, você verá à esquerda da IDE, na árvore do projeto, as opções appGradle Scripts. Isso ocorre porque estamos utilizando a visão Android, que agrupa muitas coisas e mostra os arquivos de uma forma mais significativa para Android. Ok, um pouco de mágica e esses “açúcares” são bons, mas no início, quando estamos entendendo o que de fato está acontecendo, é melhor ver as coisas na sua forma mais crua, para que a mágica deixe de ser mágica. Sempre que tiver algo “mágico”, vamos trabalhar pra entender como a mágica é feita. Quebra um pouco o encanto, mas te possibilita de fazer suas próprias mágicas depois 😉

Vamos então mudar a visão de Android para Project, que exibirá a estrutura real de pastas do projeto.

Vamos navegar então até a pasta java, no caminho app/src/main/java. O caminho para a pasta tem este formato devido a estrutura de um projeto Gradle. O Gradle uma ferramenta bastante popular que nos ajuda a gerenciar todo o ciclo de build do projeto, incluindo suas dependências e a orquestração das ferramentas de compilação e tooling em geral. Em um momento oportuno analisaremos o Gradle mais a fundo. Nesta pasta você pode ver, por enquanto, apenas a declaração do pacote do aplicativo. Vamos criar então, a nossa primeira Activity. Clique com o botão direito sobre o pacote (no meu caso, net.rafaeltoledo.tutorial) e selecionar a opção New -> Java Class e nomear a classe a ser criada de MainActivity.

Activities no Android são a representação de uma tela. É através dela que conteúdo das telas é exibido e as interações de interface são executadas. Dentre os principais componentes do Android, é o que corresponde a UI dos aplicativos.

Para que esta classe se torne uma Activity, ela deve estender da classe Activity do Android. Porém, para o nosso caso, vamos fazer a nossa classe estender AppCompatActivity. Por quê? A AppCompatActivity é uma versão da Activity que garante um comportamento uniforme a partir da versão 7 (2.1) do Android. Essa classe faz parte das bibliotecas de suporte, que conheceremos e entenderemos mais a fundo em um post futuro.

Para que a nossa Activity esteja disponível no launcher do dispositivo, na forma de um ícone na lista de aplicativos, precisamos configurar o nosso Android Manifest. O Android Manifest é um arquivo que corresponde a uma espécie de índice do nosso aplicativo. Nele é que serão descritas, dentre outras informações, quais Activities nosso aplicativo possui e quais permissões ele necessita para ser executado.

No caso, como não selecionamos a criação automática da Activity quando criamos o projeto, temos apenas a descrição básica do nosso aplicativo no arquivo AndroidManifest.xml, localizado na pasta app/src/main (podem haver pequenas diferenças, dependendo da versão do Android Studio que você estiver usando):

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.rafaeltoledo.tutorial">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

    </application>

</manifest>

Basicamente, temos aqui duas informações importantes:

  • A declaração do nó principal manifest com o atributo package. Esse atributo é importante, pois ele indica em qual pacote Java está a classe R. Esta classe, gerada automaticamente a cada compilação, é um índice com todos os recursos do nosso aplicativo que não são código Java. Veremos recursos muito em breve.
  • A declaração do nó application, que configura e descreve o nosso aplicativo. Dentre as informações principais, temos aqui o ícone, o label (que será o nome do aplicativo no launcher do sistema) e o tema.

Para adicionarmos a nossa Activity, vamos adicionar um novo nó dentro do nó application, dessa forma:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.rafaeltoledo.tutorial">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

</manifest>

Neste nó, estamos adicionando uma Activity com o nome .MainActivity. Este caminho pode ser absoluto (net.rafaeltoledo.tutorial.MainActivity) ou, como é o caso, relativo ao package que temos na raiz do manifesto. Perceba que temos também uma configuração dentro do nó activity, chamada de intent-filter.

O Android possui um mecanismo de troca de mensagens conhecido como Intents. Uma intent indica a intenção de se fazer alguma coisa, seja enviar uma mensagem para um componente, seja iniciar um componente. Essa intent pode conter diversas informações, como o destinatário, uma ação (action), uma categoria (category) e outras várias informações extras.

Lembra que quase todo aplicação que a gente escreve em qualquer linguagem sempre começa com um método main()? A ideia é parecida aqui: quando o ícone do aplicativo no launcher é tocado, é disparada uma Intent para a Activity configurada, com a categoria android.intent.category.LAUNCHER e a ação android.intent.action.MAIN, indicando que o aplicativo deve foi iniciado a partir do launcher do sistema. Existem, inclusive, aplicativos que possuem mais de um ícone na lista de apps do launcher.

Para vermos isso acontecendo na prática, vamos ver esse Intent chegando para a nossa Activity. Para isso, vamos sobrescrever o método onCreate() da nossa Activity MainActivity. Como estendemos de AppCompatActivity, herdamos vários métodos que nos permitem fazer diversas coisas no Android. O método onCreate() é um dos callbacks do ciclo de vida de uma Activity. Como você pode perceber, não chamamos o construtor da Activity diretamente, quem faz esse gerenciamento é o próprio Android. Então, somos apenas notificados quando cada coisa acontece. Você pode ver o ciclo de vida da Activity completo aqui, mas não se preocupe muito com isso por enquanto. Cada um desses estados vai fazer sentido mais adiante 🙂

No método onCreate(), vamos então pegar as informações que vieram da Intent que dispararam a nossa Activity. Para isso, vamos escrever no log do Android (conhecido como LogCat) algumas informações da Intent, como a ação, a categoria e o componente que foi lançado. O código fica assim:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent intent = getIntent();
        Log.d("MA::Action", intent.getAction());
        for (String category : intent.getCategories()) {
            Log.d("MA::Category", category);
        }
        Log.d("MA::Component", intent.getComponent().getClassName());
    }
}

Repare que, para exibir informação no Logcat, em vez do padrão System.out do Java, utilizamos a classe Log do Android. Com essa classe, temos melhores mecanismos para controlar o nível do log (error, warning, debug, etc) e podemos adicionar uma tag (primeiro parâmetro) para que fique mais fácil de encontrarmos o que queremos 🙂

Agora, se você executar o aplicativo e clicar na aba Android Monitor (na parte inferior do Android Studio), poderá conferir o log do seu aplicativo, exibindo as informações da Intent:

Bom, por hoje é só isso pessoal! No próximo post vamos entender melhor os resources do Android e criar nosso primeiro layout para a MainActivity!

Ah, sim, o projeto completo desenvolvido até aqui pode ser encontrado no Github! 🙂

Criando um Novo Projeto e Criando um Emulador no Android Studio – Tutorial Android 2

Padrão

Olá pessoal! No último post sobre Android, vimos como configurar o ambiente para programarmos, utilizando o Android Studio. Neste post, vamos ver como criar um projeto no famoso estilo Hello World, e um dispositivo virtual para a executarmos o aplicativo. Assim, você não precisa necessariamente de um celular com Android para começar a desenvolver para a plataforma, apesar de ser bastante recomendado 🙂

Ao abrir o Android Studio, vamos selecionar a opção Start a new Android Studio project. O guia que será aberto em seguida, nos auxiliará a criar um projeto sem muito esforço. Logo na primeira tela, vamos informar alguns dados básicos, como o nome do aplicativo (que utilizaremos Hello World) e o Company Domain, que nada mais é que um domínio que você tenha. Isso ajudará a criar um identificador único para seu aplicativo. No meu caso, vou colocar rafaeltoledo.net, mas você pode, por exemplo, utilizar seu nome no Github e criar o seu próprio domínio (por exemplo, rafaeltoledo.github.io). Para este projeto, não selecionaremos o suporte a C++. Vamos guardar isso para um tutorial mais avançado 🙂

Na tela seguinte, vamos configurar os módulos que nosso projeto terá, além das versões suportadas do Android. No momento que escrevo este tutorial, a minha recomendação é utilizar a API 16 como Minimum SDK. Assim, nosso aplicativo executará em mais de 97% dos dispositivos Android atualmente ativos. Você pode acompanhar a evolução das versões neste site, atualizado mensalmente.

Neste primeiro momento, vamos selecionar apenas a opção Phone and Tablet. Também vamos deixar os outros módulos, como Wear, TV e Auto para outras oportunidades e tutoriais futuros.

Na tela seguinte, vamos selecionar a opção Basic Activity. Você perceberá que muita coisa será gerada e criada, mas não se preocupe, vamos entender o que é cada coisa ao seu tempo. Avance utilizando a opção Next.

Por fim, vamos manter o nome da Activity como MainActivity, deixando as opções padrão como estão. Clique em Finish para finalizarmos o wizard.

Aguarde alguns momentos até que as dependências do projeto sejam iniciadas e a primeira compilação seja realizada.

Para executarmos este projeto, vamos criar um emulador, conhecido como AVD (Android Virtual Device). Para isso, clique no botão AVD Manager na barra de ferramentas do Android Studio.

Na janela que aparecer, selecione a opção Create Virtual Device…, que iniciará o wizard para a criação de um novo emulador.

Na tela seguinte, será exibida uma série de modelos de celular e tablet, que servirá como base para o nosso emulador. Escolha qualquer um que desejar, como por exemplo o Nexus 5. Clique em Next para avançar.

Na tela seguinte, escolheremos a imagem que utilizaremos em nosso dispositivo. A imagem contém o sistema operacional em si. A minha recomendação é escolher uma versão mais recente, e, caso seu processador e sistema operacional seja 64 bits (provavelmente é), escolha a opção x86_64 e com as APIs do Google (with Google APIs). Para este caso, estou selecionando a versão 7.1.1. Caso a imagem ainda não tenha sido baixada, clique na opção Download.

Na última tela, temos algumas opções do dispositivo, como câmera, memória e armazenamento. Por enquanto, mantenha as opções padrão e finalize.

Feche a janela do AVD Manager e clique para executar o projeto, no botão com o formato de um play na barra de ferramentas do Android Studio.

Feito isso, aguarde alguns instantes até que o emulador seja carregado e o aplicativo compilado, e então você verá seu primeiro aplicativo Android sendo executado! 🙂

E é isso pessoal! No próximo post vamos entender melhor a estrutura de um projeto Android e começar, de fato, a colocar a mão na massa! Até lá!

 

Configurando o Ambiente de Desenvolvimento – Tutorial Android 1

Padrão

Desde o último post que explicava a configuração do ambiente de desenvolvimento para Android, tivemos pelo menos uma mudança bem drástica. Se antes utilizávamos o Eclipse, hoje utilizamos o Android Studio, uma versão da poderosa IDE IntelliJ IDEA, desenvolvida pela JetBrains.

Configuração no Windows

O primeiro passo para a instalação do ambiente no Windows, é o download da JDK (Java Development Kit). Ela nos fornecerá a base de ferramentas para o desenvolvimento Java, que será utilizada também para o desenvolvimento dos apps Android também. Você pode baixar a JDK no site da Oracle. A instalação deve ocorrer sem problemas.

O próximo passo consiste em baixarmos o Android Studio. Após baixar o .exe bundle (que já contém os itens básicos de SDK e emulador), basta seguir o procedimento de instalação normalmente como qualquer outro aplicativo.

O último passo é exportarmos as variáveis de ambiente JAVA_HOME ANDROID_HOME, para que possamos, quando necessário, também executar builds e comandos a partir do console. Primeiramente vamos exportar a ANDROID_HOME. Caso você não tenha alterado o local durante a instalação do Android Studio, o valor deve ser:

%LOCALAPPDATA%\Android\sdk

Em seguida, vamos exportar a JAVA_HOME. Novamente, se você não alterou o local da instalação do Java, o valor deve ser:

%ProgramFiles%\Java\jdk1.8.0_111

No momento que escrevo este post, a última versão do Java é a 8, revisão 111. Caso a versão que você instale seja outra, ajuste o caminho de acordo.

Por fim, vamos adicionar os binários ao Path do sistema, para que sejam acessíveis através do console. Adicione ao Path as entradas %ANDROID_HOME%\tools%ANDROID_HOME%\platform-tools%JAVA_HOME%\bin.

Para testar, valide que os comandos adbjavac são acessíveis no console.

Durante a instalação do Android Studio, também deve ter sido instalado o HAXM (caso o seu processador seja compatível). O HAXM permitirá que vocẽ execute o emulador do Android com excelente desempenho. Outra boa opção de emulador é o Genymotion, porém para usos gerais, o emulador que acompanha a SDK do Android é suficiente.

Por fim, opcional mas recomendado, instale o Git, que é hoje a ferramenta de versão mais utilizada.

Configuração no Linux

No Linux, por possuirmos um gerenciador de pacotes, a coisa fica um pouco mais fácil. Caso você esteja utilizando o Ubuntu / Linux Mint, para preparar nosso ambiente, basta instalar os seguintes pacotes:

sudo apt-get install lib32z1 lib32ncurses5 lib32stdc++6 openjdk-8-jdk qemu-kvm libvirt-bin ubuntu-vm-builder bridge-utils

Caso esteja utilizando o Fedora, os pacotes são:

sudo dnf install glibc.i686 glibc-devel.i686 libstdc++.i686 zlib-devel.i686 ncurses-devel.i686 libX11-devel.i686 libXrender.i686 libXrandr.i686 java-1.8.0-openjdk-devel qemu-kvm

Outras distribuições podem ter estes pacotes com outros nomes, mas uma rápida busca pode trazer os correspondentes. O Linux não utiliza o HAXM para aceleração dos emuladores x86 – para isso, temos o KVM, um mecanismo de virtualização ligado ao kernel (caso o seu processador suporte).

Em seguida, vamos baixar o Android Studio. Para Linux, o arquivo tem extensão .tar.gz e deve ser extraído para alguma pasta (como por exemplo, ~/Android). Feito isso, basta executar o arquivo studio.sh que encontra-se dentro da pasta bin. Na primeira execução, serão baixados os pacotes necessários para a SDK. Para encerrar, selecione no menu Configure a opção Create Desktop Entry para facilitar a execução do Android Studio a partir do seu menu de aplicativos (seja UnityGnomeKDE, etc.)

O último passo é exportarmos a variável ANDROID_HOME e colocar os binários no Path. Para isso abra o arquivo ~/.bashrc ou, caso esteja utilizando o zshell, ~/.zshrc, e adicione as seguintes linhas. Caso tenha modificado o local da instalação da SDK do Android, ajuste o caminho:

export ANDROID_HOME="$HOME/Android/Sdk"
export PATH="$PATH:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools"

Por fim, é opcional porém recomendada, a instalação do Git para controle de versão dos nossos projetos.

Configuração no OSX

No OSX (Mac), a instalação é semelhante a que fizemos para o Windows. Primeiramente, baixe a JDK do site da Oracle. A instalação deve ocorrer sem maiores problemas, bastando seguir o wizard com o passo a passo da instalação.

Em seguida, baixe o Android Studio. A partir do arquivo .dmg, a instalação é tranquila, seguindo o padrão para outros aplicativos do Mac. Ao executar pela primeira vez, as ferramentas serão baixadas e o HAXM será instalado.

O último passo é a configuração das variáveis de ambiente. Contando que os caminhos padrão tanto do Java quanto da SDK do Android não foram alterados durante a instalação, adicione o seguinte conteúdo ao arquivo ~/.bash_profile (caso o arquivo não exista, crie-o).

export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home
export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$JAVA_HOME/bin:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools

Por último, opcionalmente mas fortemente recomendado, instale o Git (opte por utilizar o Homebrew, para instalar a versão mais recente).


E é isso! Aguardem os próximos posts, pois começaremos a colocar a mão na massa 🙂

Nova Série de Tutoriais Android

Padrão

Olá pessoal! Quanto tempo, hein?

Nem parece, mas o tempo passa, e como passa! Os tutoriais de Android, que além de ajudar muita gente nesses últimos anos, também transformaram minha carreira e minha vida, já estão completando incríveis 5 anos! E como todos sabem, em se tratando de tecnologia, 5 anos é uma eternidade.

Pensando nisso, nesse ano de 2017 vou publicar a série novamente, revisitando, ampliando, e atualizando todo o seu conteúdo. Afinal, de 2012 pra cá muita coisa mudou, e o Android evoluiu como ninguém podia imaginar, não é verdade?

Então aguardem, pois em breve teremos muitas novidades aqui no blog!

MVA  –  Quando Menos é Mais

Padrão

*Este post foi originalmente publicado no Medium. Confira aqui.

Desde que comecei a programar profissionalmente, sete anos atrás, arquitetura sempre foi um dos assuntos que mais me chamaram a atenção. Até hoje me instiga a ler, pesquisar, experimentar e aprender muito com tudo isso.

Quando fiz a migração definitiva do Java backend para o Android, uma das coisas que mais me chamou a atenção é que tudo estava ainda sendo desenvolvido (desde as ferramentas e IDEs até as próprias ideias sobre organização do código em si). A arquitetura do próprio Android não chega a ser um primor, e muita gente vem trabalhando em melhores formas de se organizar um app sem tornar a tarefa da manutenção um trabalho árduo e desgastante.

Nessa vertente, duas abordagens surgiram há algum tempo e ganharam força nos últimos tempos: a MVP e a Clean Architecture. Não vou abordar conceitualmente as duas arquiteturas aqui, pois são assuntos que valem (mais de) um post inteiro cada. Inclusive, falei sobre várias abordagens e opiniões no Android Dev Conference 2015, e o Medium do Android Dev BR já teve posts sobre elas também (MVC/MVP e Clean Architecture).

Em ambas as abordagens criam-se camadas para aumentar a abstração e facilitar o isolamento de cada uma das partes do sistema, de acordo com suas responsabilidades. A separação em si melhora a capacidade de testar as partes de forma isolada, pois o uso de interfaces é bastante comum, o que facilita o mock para os testes e permite testar fluxos inteiros em alguns casos utilizando apenas testes unitários.

É aí que o negócio começou a me incomodar. Principalmente na abordagem da Clean Architecture, mesmo uma funcionalidade simples exige a criação de várias camadas e classes, adicionando uma camada de burocratização na aplicação. Não estou dizendo que ela não traga nenhum benefício, mas esse problema me incomoda e muito nos projetos que vejo utilizando esta arquitetura.

O MVP sofre menos com esse problema, mas ainda exige a criação de uma série de interfaces com as abstrações de cada uma das camadas, com a justificativa de melhorar a testabilidade do sistema. O meu problema principal neste caso é a completa ausência de testes (e testes relevantes) nas mais diversas implementações do padrão que já encontrei. Se as abstrações são a troca a ser realizada para que se tenha uma aplicação testável, por que o objetivo não está aparecendo no final?

Ainda relacionado ao MVP, um paradigma criado pensando-se no ambiente da Web que surgiu com força por volta de 2007, alguns dos benefícios não se aplicam tanto ao Android, como a possibilidade de troca transparente da implementação de View (trocar uma implementação de Activity por Fragment não chega a ser um problema em qualquer que seja a arquitetura implementada), ou mesmo a troca dos modelos por mocks para propósitos de demonstração ou testes — é possível implementar cenários como o “development drawer” de forma agnóstica.

Conversando com algumas pessoas e vendo alguns projetos sendo desenvolvidos, vi que muitas vezes as arquiteturas são implementadas sem entender os conceitos, simplesmente reproduzindo o que foi visto em algum outro projeto. Muitas vezes faltam conceitos, mas principalmente um pouco de questionamento: preciso de tudo isso?

No meio de tantos Ms e Vs, acabei me deparando com o MVA — o Minimum Viable Architecture. Uma das frases que definem o MVA diz que:

O melhor código que você pode escrever agora é o código que você irá jogar fora em alguns anos.

Apesar de ser um conceito voltado muito mais para o desenvolvimento de produtos do que programação em si, ele serve para questionarmos um pouco a forma como desenvolvemos os nossos apps hoje.

  • A arquitetura não é construída pensando nos piores cenários. Em vez disso, o produto é construído pensando nos cenários mais comuns.
  • Escreva código em pequenos incrementos através do tempo. Para que isso aconteça, as definições principais de requisitos e tecnologias já devem ter sido definidas.
  • Tudo que é desenvolvido deve ser baseado em requisitos concretos ou pelo menos razoáveis, não sobre achismos ou deduções.

Entendam estes conceitos como uma forma de implementações mais objetivas. Isso não quer dizer que a qualidade e organização deve ser deixada de lado. Nem que os testes devem ser ignorados nem que a aplicação não deve ter uma arquitetura e padrões definidos. Mas pensem e questionem sobre o que está sendo feito. A troca está valendo a pena? Quais objetivos estão sendo buscados com isso?

Enfim, essa é uma grande e excelente conversa, que só estou começando aqui. O Android está evoluindo e a nossa forma de pensar a respeito das nossas aplicações também deve evoluir. Não sou contra os vários MVs, mas o questionamento faz-se necessário.

E você, o que acha? Vamos conversar sobre arquitetura? 🙂

Gerando o Relatório de Cobertura de Testes Unificado com Jacoco, Robolectric e Espresso

Padrão

Olá, pessoal! No post de hoje, veremos como é possível gerar um relatório de cobertura de testes em um projeto Android incluindo tanto os testes unitários (geralmente escritos com JUnit, Mockito, Robolectric, etc.) quanto os instrumentados (geralmente escritos utilizando o Espresso).

Relatórios de cobertura de testes são uma ferramenta muito importante para mensurar o quanto nossos testes realmente exercitam nosso código. Apesar de não serem a garantia de um software sem bugs, ter uma porcentagem alta de cobertura pode evitar muitas dores de cabeça no desenrolar do projeto.

No Android, para gerar esse tipo de relatório utilizamos o Jacoco (Java Code Coverage), uma das ferramentas mais utilizadas dentro do Java para esse propósito. No ambiente de desenvolvimento do Android, temos um fator dificultador que é o fato de possuirmos dois artefatos de teste diferentes, geralmente representados pelas pastas test (unitários) eandroidTest (instrumentados).

Primeiramente, vamos gerar o relatório de cobertura dos testes instrumentados do Espresso. Para este exemplo, teremos uma Activity bem simples:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private TextView text;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.button).setOnClickListener(this);
        findViewById(R.id.hide).setOnClickListener(this);
        text = (TextView) findViewById(R.id.text);
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.button) {
            text.setText("Hello World!");
        } else {
            v.setVisibility(View.GONE);
        }
    }
}

O layout dessa Activity é o seguinte:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello" />

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Click Me!" />

    <Button
        android:id="@+id/hide"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Don't Click Me!" />

</LinearLayout>

Vamos então criar um teste no Espresso para garantir que o texto do TextView text seja modificado para Hello World! ao clicarmos no Button button:

@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainActivityTest {

    @Rule
    public ActivityTestRule<MainActivity> rule = new ActivityTestRule<>(MainActivity.class);

    @Test
    public void shouldUpdateTextAfterButtonClick() {
        onView(withId(R.id.button)).perform(click());

        onView(withId(R.id.text)).check(matches(withText("Hello World!")));
    }
}

Após a execução desse teste, temos o relatório de execução:

Captura de tela de 2015-12-22 13-58-54

Porém, o relatório de cobertura ainda não é gerado. Para habilitarmos essa opção, precisamos adicionar uma propriedade para a nossa build variant de debug. Na DSL do plugin do Android, habilite a cobertura por meio da propriedade testCoverageEnabled:

android {
    ...
    buildTypes {
        debug {
            testCoverageEnabled true
        }
        ...
    }
}

Agora, basta executar a task createDebugCoverageReport para que os testes sejam executados e o relatório seja gerado.

Captura de tela de 2015-12-22 14-12-19

Perfeito! Já temos o relatório de cobertura do nosso projeto.

Vamos agora criar um teste usando o Robolectric para testar o else da lógica da nossa Activity. Porém, um aviso: particularmente, eu não recomendo testar a Activity e componentes do Android diretamente pelo Robolectric. Prefira testes que, de fato, testem unidades de código.

Um teste com Robolectric que testa o comportamento do botão hide, cuja visibilidade é alterada quando clicado, ficaria assim:

@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21)
public class MainActivityTest {

    @Test
    public void shouldHideButtonAfterClick() {
        MainActivity activity = Robolectric.setupActivity(MainActivity.class);

        Button button = (Button) activity.findViewById(R.id.hide);
        button.performClick();

        assertThat(button.getVisibility(), is(View.GONE));
    }
}

Por padrão, o plugin do Android só gera o relatório de cobertura dos testes instrumentados. Para que seja possível gerar a cobertura dos testes unitários, é necessário criar uma task de execução do relatório manualmente:

apply plugin: 'jacoco'

task jacocoTestReport(type: JacocoReport, dependsOn: 'testDebugUnitTest') {

    reports {
        xml.enabled = true
        html.enabled = true
    }

    jacocoClasspath = configurations['androidJacocoAnt']

    def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*']
    def debugTree = fileTree(dir: "${buildDir}/intermediates/classes/debug", excludes: fileFilter)
    def mainSrc = "${project.projectDir}/src/main/java"

    sourceDirectories = files([mainSrc])
    classDirectories = files([debugTree])
    executionData = files("${buildDir}/jacoco/testDebugUnitTest.exec")
}

Com a criação da task jacocoTestReport, temos agora a geração do relatório de cobertura também para os testes unitários.

Captura de tela de 2015-12-22 14-26-25

Porém, permanece o problema: como ter a cobertura unificada do resultado dos dois grupos de testes?

A instrumentação realizada pelo Jacoco produz arquivos de execução que contêm os dados necessários para a criação do relatório (HTML, XML, etc). O grande problema é que o Espresso gera o arquivo .ec, enquanto que a execução dos testes unitários gera o arquivo .exec, ou seja, temos formatos de arquivos diferentes!

E agora? Como converter de um formato para o outro? A resposta, obtida depois de muita pesquisa, é: simplesmente não é necessário converter!

Como não temos acesso à task que configura a execução dos testes com o Espresso, precisamos garantir que ela seja executada primeiro, para que, na execução do relatório dos testes unitários, o arquivo coverage.ec já esteja disponível.

Na task do relatório dos testes unitários, o que vamos fazer é adicionar o arquivo coverage.ec também como parâmetro na propriedade executionData:

apply plugin: 'jacoco'

task jacocoTestReport(type: JacocoReport, dependsOn: 'testDebugUnitTest') {

    reports {
        xml.enabled = true
        html.enabled = true
    }

    jacocoClasspath = configurations['androidJacocoAnt']

    def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*']
    def debugTree = fileTree(dir: "${buildDir}/intermediates/classes/debug", excludes: fileFilter)
    def mainSrc = "${project.projectDir}/src/main/java"

    sourceDirectories = files([mainSrc])
    classDirectories = files([debugTree])
    executionData = files(["${buildDir}/jacoco/testDebugUnitTest.exec",
                           "${buildDir}/outputs/code-coverage/connected/coverage.ec"
    ])
}

Por fim, ao executarmos a task completa, com os testes instrumentados pelo Espresso primeiro, teremos o relatório unificado de cobertura.

gradle clean createDebugCoverageReport jacocoTestReport

Captura de tela de 2015-12-22 14-39-29

E é isso! O projeto de exemplo pode ser encontrado neste repositório do Github! #KeepCoding #KeepTesting