The Social Project: Configurando a Koin para DI

Padrão

Se você ainda não conhece as motivações deste projeto, leia o primeiro artigo aqui.

Olá pessoal! No último post integramos a métrica de cobertura de código ao nosso projeto. Hoje, vamos começar a mexer um pouco mais com o código da nossa aplicação, começando pela integração de uma biblioteca para nos fornecer um mecanismo de injeção de dependências (ou Dependency Injection – DI).

Quando falamos de DI no Android, logo nos vem a ideia de utilizar o Dagger para tal tarefa. Apesar de ser uma ferramenta muito poderosa, com estabilidade comprovada em produção, ela é um pouco verbosa e com uma quantidade relativamente grande de boilerplate para configurarmos – principalmente quando levamos em consideração um projeto 100% Kotlin como é o nosso caso.

Pensando, então, em um contexto puramente Kotlin, temos duas bibliotecas em evidência atualmente na comunidade, a Kodein e a Koin. A Kodein é uma solução mais robusta, focada em multi-plataforma (pensando nos diversos targets do Kotlin, como JVM, nativo e JavaScript), com uma recente refatoração na versão 5. Já a Koin tem uma abordagem mais minimalista e, ao meu ver, um ponto bem positivo: a integração com os Architecture Components (AC) do Android.

Sem pensar em otimizações prematuras, e imaginando a evolução do projeto, vamos fazer um setup inicial desse nosso mecanismo de fornecimento de dependências, com uma integração inicial com os AC. Eles vão nos ajudar a manter nossas Activities e Fragments mais limpos, isolar lógicas e facilitar testes.

O primeiro passo aqui, será adicionar as dependências da Koin e dos Architecture Components (no nosso caso, ViewModel e LiveData). Vamos adicionar as versões no arquivo build.gradle e as dependências no app/build.gradle.

// build.gradle
buildscript {
  ext.versions = [
    'kotlin': '1.2.41',
    'supportLibrary': '27.1.1',
    'jacoco': '0.8.1',
    'archComponents': '1.1.1',
    'koin': '0.9.3',
  ]
  ...    
}
// app/build.gradle
apply plugin: 'com.android.application'
apply plugin: 'org.jetbrains.kotlin.android'
apply plugin: 'org.jetbrains.kotlin.kapt'

...

dependencies {
  ...

  implementation "android.arch.lifecycle:extensions:$versions.archComponents"
  kapt "android.arch.lifecycle:compiler:$versions.archComponents"

  implementation "org.koin:koin-android:$versions.koin"
  implementation "org.koin:koin-android-architecture:$versions.koin"
}

Como os AC necessitam de uma dependência de processador de anotações (annotationProcessor), precisamos aplicar o plugin do kapt (o processador de anotações do Kotlin), e adicionar a dependência do compiler com o escopo kapt.

Primeiramente vamos criar um ViewModel de exemplo, quase um placeholder. Como ainda não temos nenhuma feature no nosso app, faremos isso para validar em parte nossa arquitetura com o Koin. Sendo assim, criei um MainViewModel que será utilizado pela nossa MainActivity (que até então está vazia).

Na nossa Activity, vamos pedir uma instância desse ViewModel através do Koin e (por enquanto), simplesmente exibir o conteúdo do método getString() em um TextView.

Para que essa “mágica” aconteça – e que o Koin seja capaz de nos fornecer uma instância de qualquer ViewModel -, precisamos configurar os módulos, e ensinar a biblioteca a construir esses objetos.

Primeiramente, perceba que o nosso ViewModel recebe uma String como parâmetro de seu construtor. Para que o Koin nos forneça um objeto do ViewModel, precisamos de alguma forma fornecer esse valor. Para isso, criarei um pacote chamado di, contendo dois arquivos: FirstModule (que fornecerá essa String) e ViewModelModule (que fornecerá a instância do ViewModel).

Para criar os módulos, faremos uso da função applicationContext do Koin:

Para o valor do tipo String, utilizamos bean, enquanto que, para o ViewModel, utilizamos viewModel. Para fornecer os parâmetros necessários a criação dos objetos (no caso aqui, do ViewModel), basta passarmos get() como parâmetro.

Por final, para amarrarmos todas as pontas, basta configurarmos o Koin na classe Application. Como ainda não temos uma implementação própria, vamos criar uma classe SocialApp para que possamos fazer essa inicialização.

A inicialização do Koin se dá por meio da extension function startKoin(), para a qual passamos a lista de módulos – no nosso caso, viewModelModulefirstModule. É importante lembrar de adicionar a nossa implementação da Application ao AndroidManifest.

O resultado disso é a nossa string exibida corretamente na MainActivity.

Tudo pronto? Ainda não! Cadê os testes?

Todo esse overhead de configuração é inútil se não estamos utilizando com algum propósito. Além de deixar nossas classes mais enxutas, a ideia é facilitar a troca de objetos por mocks ou mesmo por valores controlados para a execução dos testes.

Apesar de não ser o ideal, por enquanto vamos continuar com o Robolectric. Como nosso setup do Koin está localizado na Application, precisamos substituir ou alterar a sua implementação para que possamos preparar a execução dos testes. Nesse aspecto, o Robolectric possui uma facilidade muito bacana: basta criar uma classe com o mesmo nome da sua implementação de Application, utilizando o prefixo Test. Com isso, ele automaticamente utilizará a versão de testes da sua Application durante a execução.

Sendo assim, dentro do nosso source set de testes, criarei um arquivo chamado TestSetup.kt com o seguinte conteúdo:

Aqui, a ideia é deixar o valor a ser injetado configurável, para que possamos controlá-lo durante a execução dos testes. Assim, um teste que verifica se o valor inserido foi exibido corretamente na tela ficaria:

Para deixar as asserções mais fluídas, estou utilizando o Truth.

Ao abrir um Pull Request para o repositório, temos, então, uma surpresa!

O Coveralls percebeu que tivemos uma queda na cobertura de código (de 100% para 65%) e nos avisou! Esse é exatamente o intuito de mapear essa métrica, para percebermos o quanto um determinado código está impactando na cobertura de testes. Por mais que ter uma cobertura de código alta não seja sinônimo de qualidade, ela pode indicar que estamos escrevendo código sem testar, o que definitivamente não é legal!

Nesse caso específico, tínhamos a utópica marca de 100% porque tínhamos apenas uma classe sem código algum, então a queda já era esperada.

Bom, por hoje é isso! O PR com essas modificações foi aberto, aprovado e mergeado. Vale salientar aqui que a implementação inicial teve uma crítica bem legal do Victor Nascimento. A ideia é exatamente essa, os PRs devem gerar discussões técnicas relevantes.

Até a próxima!

The Social Project: Configurando Kotlin no projeto

Padrão

Olá pessoal!

Se você ainda não conhece as motivações deste projeto, leia o primeiro artigo aqui.

No último post, configuramos o CircleCI para observar as branches do nosso projeto, bem como a abertura de pull requests no repositório. Hoje, vamos olhar um pouco mais o projeto e começar a estruturá-lo para posteriormente iniciarmos a implementação de fato.

O projeto em si foi criado a partir do template padrão do Android Studio 3.0.1, sem qualquer modificação na sua estrutura, e sem a inclusão do Kotlin por padrão. Meu intuito é entendermos o que é necessário para transformarmos um projeto Android com Java em um projeto Android com Kotlin.

O primeiro passo aqui é ajustar o arquivo build.gradle principal, aquele localizado na raiz do projeto. Inicialmente, vamos remover os comentários vindos do template. Eles são informativos sobre como configurar as dependências do projeto, porém podemos removê-los tranquilamente, já que sabemos onde vamos colocar cada coisa.

Em seguida, vamos criar uma variável, chamada de kotlinVersion, para guardar a versão do Kotlin que estaremos utilizando no projeto. É importante que essa versão esteja separada, pois ela deve ser a mesma em todas as dependências relacionadas ao Kotlin no projeto. Aqui fica uma ressalva: perceba que utilizamos a nomenclatura com Camel Case (enquanto o template do AS para Kotlin utiliza Snake Case). O Groovy (linguagem utilizada nos scripts do Gradle) tem convenções de código muito semelhantes ao Java. Assim, utilizamos Snake Case apenas nos casos onde o valor é uma constante, e sempre em caixa alta.

Após isso, adicionamos o plugin de Kotlin para Gradle, fazendo uso da interpolação de strings para inserir a versão. Assim, nosso arquivo build.gradle ficará assim:

buildscript {

    ext.kotlinVersion = '1.2.30'
    
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

Vamos agora editar então o arquivo de build do módulo app (localizado em app/build.gradle). Nele, primeiramente aplicamos o plugin do Kotlin, seguindo a nomenclatura mais recomendada pelo Gradle – utilizando o namespace. Dessa forma, aplicamos o plugin org.jetbrains.kotlin.android. Em seguida, vamos atualizar o tooling para a última versão do Android (atualmente a API 27). Como o Android Studio 3.0 já foi lançado a algum tempo, o seu template padrão ainda aponta para a API 26. Assim, apontamos as versões de compileSdkVersiontargetSdkVersion para 27, além de atualizarmos a versão da Support Library para 27.1.0, a versão mais recente neste momento.

O próximo passo para completar a integração do Kotlin no projeto é a adição da standard library do Kotlin. Essa dependência possui 3 versões: kotlin-stdlib (Java 6), kotlin-stdlib-jre7 (Java 7) e kotlin-stdlib-jre8 (Java 8). Particularmente eu adiciono a versão jre7, primeiro pelo fato da nossa API mínima ser KitKat (que já suporta as features do Java 7). Poderíamos utilizar a jre8, porém isso faria com que o processo de desugaring ocorresse durante a compilação, o que pode impactar no nosso tempo de build.

Por fim, dois pontos de ajuste que particularmente recomendo por questões de organização. O primeiro é a adição de source sets específicos para Kotlin. Quando temos um projeto Java, os arquivos fonte geralmente ficam na pasta src/main/java. Assim, adicionamos aos source sets src/main/kotlin. Ao meu ver, essa organização é particularmente útil quando o projeto acaba tendo arquivos de ambas as linguagens (o que não deve ocorrer no nosso caso) e para que a estrutura de diretórios fique mais semântica. O segundo ponto de ajuste, mais estético, é a padronização do uso de aspas simples e duplas no arquivo. Aspas duplas, somente quando houver interpolação na string.

Nosso arquivo, então, fica dessa forma:

apply plugin: 'com.android.application'
apply plugin: 'org.jetbrains.kotlin.android'

android {
    compileSdkVersion 27

    defaultConfig {
        applicationId 'net.rafaeltoledo.social'
        minSdkVersion 19
        targetSdkVersion 27
        versionCode 1
        versionName '1.0'

        testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
    }

    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
        test.java.srcDirs += 'src/test/kotlin'
        androidTest.java.srcDirs += 'src/androidTest/kotlin'
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation 'com.android.support:appcompat-v7:27.1.0'

    implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlinVersion"

    testImplementation 'junit:junit:4.12'

    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}

Aproveitando que estamos modificando as configurações de build do nosso projeto, recomendo atualizarmos também a versão do Gradle que está sendo utilizada. Para isso, basta gerarmos o wrapper apontando para a versão mais recente (4.6 no momento em que escrevo este post). A não ser quando há imcompatibilidades entre o plugin do Android e o Gradle, sempre recomendo a utilização da versão mais recente do Gradle, já que ela costuma trazer melhorias importantes de performance (o que é sempre bem-vindo). Para atualizar o wrapper, basta executar o seguinte comando:

./gradlew wrapper --gradle-version 4.6 --distribution-type all

Para que nosso aplicativo já tenha uma Activity inicial, também adicionei uma MainActivity vazia e adicionei ao Android Manifest. Também aproveitei e exclui os testes que vem por padrão no template do Android Studio – assim que começarmos a entrar nas features do app, escreveremos os nossos 🙂

Seguindo o nosso Git Flow, criei um Pull Request para a branch develop, que, assim que passou no CI, foi mergeado 🙂

Por hoje é isso. Apesar deste post não mostrar nada muito novo, acho importante configurarmos o Kotlin por nós mesmos e saber o que muda no projeto. Afinal, os build scripts também são código, e mantê-los organizados faz parte da saúde do projeto. Aguardem que no próximo post teremos mais coisas interessantes.

Até lá!

Desenvolvendo para Android com Kotlin: Como Começar?

Padrão

Olá pessoal!

Acredito que alguns de vocês já tenham ouvido falar no Kotlin. Pra quem não conhece, o Kotlin é uma linguagem que vem sendo desenvolvida pela Jetbrains (a empresa responsável pelo Android Studio, IntelliJ IDEA e uma porção de IDEs muito boas), focada na JVM, Android e web. A princípio, ela pode ser vista como um Java menos (muito) verboso. Por alguns, ela é chamada o Swift do Android! 🙂

Apesar de ainda estar em desenvolvimento, já é possível criar aplicativos completos utilizando essa linguagem, e é exatamente esse o propósito desse post: mostrar como configurar a sua IDE e criar o primeiro projeto usando Kotlin.

Bom, o primeiro passo é instalar os plugins do Kotlin no seu Android Studio ou IntelliJ. Para isso, abra as configurações da IDE e localize a opção Plugins. Ali, clique em Install JetBrains plugin… e localize os plugins Kotlin e Kotlin Extensions for Android.

Kotlin PluginsFeito isso, basta reiniciar a sua IDE para que a instalação esteja completa.

Com isso, já podemos criar o nosso projeto Android. A criação em nada difere de um projeto comum. Como ainda não temos um template, no início, temos que converter as classes manualmente. O que difere aqui são os arquivos de configuração do Gradle, que terão os plugins do Kotlin adicionados:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.3.1'
        classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:0.12.1230'
        classpath 'org.jetbrains.kotlin:kotlin-android-extensions:0.12.1230'
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

Aqui, adicionamos ao classpah os plugins do Kotlin (kotlin-gradle-plugin e kotlin-android-extensions – que não será utilizado ainda nesse primeiro tutorial).

No build.gradle do projeto, aplicamos o plugin e adicionamos a biblioteca padrão do kotlin:

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'

android {
    compileSdkVersion 23
    buildToolsVersion '23.0.1'

    defaultConfig {
        applicationId 'net.rafaeltoledo.hellokotlin'
        minSdkVersion 16
        targetSdkVersion 23
        versionCode 1
        versionName '1.0'
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.0.1'
    compile 'org.jetbrains.kotlin:kotlin-stdlib:0.12.1230'
}

Caso tenha escolhido para que o Android Studio gere a primeira Activity, você pode convertê-la para Kotlin indo até o menu Code -> Convert Java File to Kotlin File. Aquela sua Activity marota em Java vai virar algo mais ou menos assim:

package net.rafaeltoledo.hellokotlin.activity

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import net.rafaeltoledo.hellokotlin.R

public class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main)
    }
}

Agora é só executar e é só alegria. Até a versão 0.11, era necessária uma pasta de sources específica para o Kotlin, mas com a 0.12+, já não é necessário (perceba que os arquivos .kt ficam na pasta java de sources).

É isso pessoal! Aguardem que logo logo eu trago mais novidades!