Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Kotlin no Android: Desbravando as oportunidades de ponta a ponta!

Kotlin no Android: Desbravando as oportunidades de ponta a ponta!

This talk was first held during Kotlin Community Summit '18, the first Kotlin conference in São Paulo, that happened on September 18, 2018, in São Paulo, Brazil.

In this talk I talked a little about some possibilities and opportunities that we can have in using the Kotlin language in an Android project.
From the construction of interfaces to asynchronous processes and going through the tests, I showed how we can extract the most from this language in a modern project.

More info: https://eventos.imasters.com.br/kotlinsummit/

Walmyr Carvalho

September 15, 2018
Tweet

More Decks by Walmyr Carvalho

Other Decks in Technology

Transcript

  1. Walmyr Carvalho
    Kotlin no Android:
    Desbravando as oportunidades de ponta a ponta!
    +

    View full-size slide

  2. Walmyr Carvalho
    @walmyrcarvalho

    View full-size slide

  3. Um fato: Kotlin veio pra ficar! ❤

    View full-size slide

  4. 2ª linguagem mais amada - StackOverflow Developer Survey 2018
    bit.ly/2Mc6HMa

    View full-size slide

  5. Kotlin
    - Criada pela JetBrains e anunciada publicamente em 2012
    - Open Source (Apache 2.0)
    - Suporta compilação pra JVM, JS e nativo
    - Null-safety por design

    View full-size slide

  6. E como a gente pode usar Kotlin nas nossas aplicações?

    View full-size slide

  7. Será que podemos ir mais longe com Kotlin no Android?

    View full-size slide

  8. Oportunidades

    View full-size slide

  9. Podemos utilizar Kotlin em diversas
    camadas da nossa aplicação Android:

    View full-size slide

  10. Geral
    UI
    Async
    Testes
    Build

    View full-size slide

  11. Kotlin + Android KTX
    Anko + Kotlin Android
    Extensions (kotlinx)
    Coroutines
    Spek + MockK
    Geral
    UI
    Async
    Testes
    Build
    Gradle Kotlin DSL +
    Detekt

    View full-size slide

  12. O Android KTX é um conjunto de extension functions criadas pelo Google
    para otimizar o desenvolvimento de tarefas comuns do Android.

    View full-size slide

  13. Android KTX - GitHub
    github.com/android/android-ktx

    View full-size slide

  14. Módulo (artefato) Versão Pacotes
    androidx.core:core-ktx 1.0.0-alpha1 Todos os pacotes core abaixo
    androidx.fragment:fragment-ktx 1.0.0-alpha1 androidx.fragment.app
    androidx.palette:palette-ktx 1.0.0-alpha1 androidx.palette.graphics
    androidx.sqlite:sqlite-ktx 1.0.0-alpha1 androidx.sqlite.db
    androidx.collection:collection-ktx 1.0.0-alpha1 androidx.collection
    androidx.lifecycle:lifecycle-viewmodel-ktx 2.0.0-alpha1 androidx.lifecycle
    androidx.lifecycle:lifecycle-reactivestreams-ktx 2.0.0-alpha1 androidx.lifecycle
    android.arch.navigation:navigation-common-ktx 1.0.0-alpha01 androidx.navigation
    android.arch.navigation:navigation-fragment-ktx 1.0.0-alpha01 androidx.navigation.fragment
    android.arch.navigation:navigation-runtime-ktx 1.0.0-alpha01 androidx.navigation
    android.arch.navigation:navigation-testing-ktx 1.0.0-alpha01 androidx.navigation.testing
    android.arch.navigation:navigation-ui-ktx 1.0.0-alpha01 androidx.navigation.ui
    android.arch.work:work-runtime-ktx 1.0.0-alpha01 androidx.work.ktx
    Fonte: developer.android.com/kotlin/ktx

    View full-size slide

  15. repositories {
    google()
    }
    build.gradle

    View full-size slide

  16. dependencies {
    implementation ‘androidx.core:core-ktx:$ktx_version’
    }
    app/build.gradle

    View full-size slide

  17. // Kotlin
    db.beginTransaction()
    try {
    // insira os dados
    db.setTransactionSuccessful()
    } finally {
    db.endTransaction()
    }
    // Kotlin + Android KTX
    db.transaction {
    // insira os dados
    }

    View full-size slide

  18. Mas… como é a implementação disso?

    View full-size slide

  19. inline fun SQLiteDatabase.transaction(
    exclusive: Boolean = true,
    body: SQLiteDatabase.() -> T
    ): T {
    if (exclusive) {
    beginTransaction()
    } else {
    beginTransactionNonExclusive()
    }
    try {
    val result = body()
    setTransactionSuccessful()
    return result
    } finally {
    endTransaction()
    }
    }
    SQLiteDatabase.kt

    View full-size slide

  20. // Kotlin
    sharedPreferences.edit()
    .putBoolean("key", value)
    .apply()
    // Kotlin + Android KTX
    sharedPreferences.edit {
    putBoolean("key", value)
    }

    View full-size slide

  21. // Kotlin
    view.viewTreeObserver.addOnPreDrawListener(
    object : ViewTreeObserver.OnPreDrawListener {
    override fun onPreDraw(): Boolean {
    viewTreeObserver.removeOnPreDrawListener(this)
    actionToBeTriggered()
    return true
    }
    }
    )
    // Kotlin + Android KTX
    view.doOnPreDraw {
    actionToBeTriggered()
    }

    View full-size slide

  22. context.withStyledAttributes(set = someAttributeSet,
    attrs = attributes,
    defStyleAttr = ...,
    defStyleRes = ...) {
    // do your rolê
    }
    context.withStyledAttributes(set = someAttributeSet, attrs =
    attributes) {
    // do your rolê
    }

    View full-size slide

  23. Gradle Kotlin DSL

    View full-size slide

  24. Sim, é isso mesmo que cê tá pensando:
    scripts .gradle escritos em Kotlin! ❤

    View full-size slide

  25. Gradle Kotlin DSL - GitHub
    github.com/gradle/kotlin-dsl

    View full-size slide

  26. Atenção: Para utilizar o Gradle Kotlin DSL,
    precisamos utilizar a versão 4.10 (ou superior) do Gradle.

    View full-size slide

  27. Para começar:

    View full-size slide

  28. rootProject.buildFileName = "build.gradle.kts"
    include(":app")
    settings.gradle.kts

    View full-size slide

  29. buildscript {
    repositories {
    google()
    jcenter()
    }
    dependencies {
    classpath("com.android.tools.build:gradle:3.1.4")
    classpath(kotlin("gradle-plugin", version = "1.2.61"))
    }
    }
    allprojects {
    repositories {
    google()
    jcenter()
    }
    }
    tasks.register("clean", Delete::class) {
    delete(rootProject.buildDir)
    }
    build.gradle.kts

    View full-size slide

  30. import org.jetbrains.kotlin.config.KotlinCompilerVersion
    plugins {
    id("com.android.application")
    id("kotlin-android")
    id("kotlin-android-extensions")
    }
    app/build.gradle.kts

    View full-size slide

  31. android {
    compileSdkVersion(27)
    defaultConfig {
    applicationId = "me.walmyrcarvalho.hellosummit"
    minSdkVersion(15)
    targetSdkVersion(27)
    versionCode = 1
    versionName = "1.0"
    testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
    getByName("release") {
    isMinifyEnabled = false
    proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
    }
    }
    }
    app/build.gradle.kts

    View full-size slide

  32. dependencies {
    implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
    implementation(kotlin("stdlib-jdk7", KotlinCompilerVersion.VERSION))
    implementation("com.android.support:appcompat-v7:27.1.1")
    implementation(“com.android.support.constraint:constraint-layout:1.1.3")
    testImplementation("junit:junit:4.12")
    androidTestImplementation("com.android.support.test:runner:1.0.2")
    androidTestImplementation("com.android.support.test.espresso:espresso-core:3.0.2")
    }
    app/build.gradle.kts

    View full-size slide

  33. Gradle Goes Kotlin - Hans Dockter (CEO, Gradle)
    youtu.be/4gmanjWNZ8E

    View full-size slide

  34. O Detekt é uma ferramenta de análise estática de código para Kotlin,
    bem completa e customizável, ideal para pipelines de CI/CD e PRs em geral.

    View full-size slide

  35. Detekt - GitHub
    github.com/arturbosch/detekt

    View full-size slide

  36. buildscript {
    repositories {
    jcenter()
    }
    }
    plugins {
    id("io.gitlab.arturbosch.detekt").version("[version]")
    }
    detekt {
    toolVersion = "[version]"
    input = files("src/main/kotlin")
    filters = ".*/resources/.*,.*/build/.*"
    }
    build.gradle

    View full-size slide

  37. Os arquivos de configuração do Detekt utilizam yaml:

    View full-size slide

  38. autoCorrect: true
    failFast: false
    test-pattern: # Configure exclusions for test sources
    active: true
    patterns: # Test file regexes
    - '.*/test/.*'
    - '.*Test.kt'
    - '.*Spec.kt'
    exclude-rule-sets:
    - 'comments'
    exclude-rules:
    - 'NamingRules'
    - 'WildcardImport'
    - 'MagicNumber'
    - 'MaxLineLength'
    - 'LateinitUsage'
    - 'StringLiteralDuplication'
    - 'SpreadOperator'
    - 'TooManyFunctions'
    - 'ForEachOnRange'
    default-detekt-config.yml

    View full-size slide

  39. build:
    maxIssues: 10
    weights:
    # complexity: 2
    # LongParameterList: 1
    # style: 1
    # comments: 1
    processors:
    active: true
    exclude:
    # - 'FunctionCountProcessor'
    # - 'PropertyCountProcessor'
    # - 'ClassCountProcessor'
    # - 'PackageCountProcessor'
    # - 'KtFileCountProcessor'
    default-detekt-config.yml

    View full-size slide

  40. console-reports:
    active: true
    exclude:
    # - 'ProjectStatisticsReport'
    # - 'ComplexityReport'
    # - 'NotificationReport'
    # - 'FindingsReport'
    # - 'BuildFailureReport'
    comments:
    active: true
    CommentOverPrivateFunction:
    active: false
    CommentOverPrivateProperty:
    active: false
    EndOfSentenceFormat:
    active: false
    endOfSentenceFormat: ([.?!][ \t\n\r\f<])|([.?!]$)
    UndocumentedPublicClass:
    active: false
    searchInNestedClass: true
    searchInInnerClass: true
    searchInInnerObject: true
    searchInInnerInterface: true
    UndocumentedPublicFunction:
    active: false
    default-detekt-config.yml

    View full-size slide

  41. Alguns conjuntos de regras incluem análises como:
    - Code smell
    - Complexidade/legibilidade
    - Disparo de exceções
    - Formatação
    - Nomenclaturas
    - Code style
    - Linhas/blocos vazios de código
    - Padrões de testes

    View full-size slide

  42. O Anko é uma biblioteca que facilita o desenvolvimento de interfaces no
    Android utilizando Kotlin, sem a necessidade de se escrever XMLs

    (além de outras coisinhas a mais).

    View full-size slide

  43. Anko - GitHub
    github.com/kotlin/anko/

    View full-size slide

  44. dependencies {
    implementation "org.jetbrains.anko:anko:$anko_version"
    }
    build.gradle

    View full-size slide

  45. O Anko é divido em quatro macro-repositórios:
    - anko-commons: Métodos comuns a todos ao Anko em geral
    - anko-layouts: Bindings gerais de layouts gerais do Anko
    - anko-coroutines: Callbacks para Anko layouts com Coroutines
    - anko-sqlite: Helpers do Anko para SQLite
    Além desses, existem repositórios para Views, ViewGroups
    e Layouts variados, como CardView, RecyclerView e mais.

    View full-size slide

  46. dependencies {
    // Anko Commons
    implementation "org.jetbrains.anko:anko-commons:$anko_version"
    // Anko Layouts
    // sdk15, sdk19, sdk21, sdk23 are also available
    implementation "org.jetbrains.anko:anko-sdk25:$anko_version"
    implementation "org.jetbrains.anko:anko-appcompat-v7:$anko_version"
    // Coroutine listeners for Anko Layouts
    implementation "org.jetbrains.anko:anko-sdk25-coroutines:$anko_version"
    implementation "org.jetbrains.anko:anko-appcompat-v7-coroutines:$anko_version"
    // Anko SQLite
    implementation "org.jetbrains.anko:anko-sqlite:$anko_version"
    }
    build.gradle

    View full-size slide

  47. override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    verticalLayout {
    padding = dip(30)
    editText {
    hint = "Name"
    textSize = 24f
    }
    editText {
    hint = "Password"
    textSize = 24f
    }
    button("Login") {
    textSize = 26f
    }
    }
    }
    MainActivity.kt

    View full-size slide

  48. linearLayout {
    button("Login") {
    textSize = 26f
    }.lparams(width = wrapContent) {
    horizontalMargin = dip(5)
    topMargin = dip(10)
    }
    }
    MainActivity.kt

    View full-size slide

  49. verticalLayout {
    editText {
    hint = "Name"
    }
    editText {
    hint = "Password"
    }
    }.applyRecursively { view -> when(view) {
    is EditText -> view.textSize = 20f
    }}
    MainActivity.kt

    View full-size slide

  50. Com o Anko também temos uma série de funções para a exibição de
    componentes (como Dialogs, ProgressBars e Toasts),
    uso de logging, uso de resources/dimensions e outros.

    View full-size slide

  51. // Toasts
    toast("Hey, I'm a toast!")
    toast(R.string.message)
    longToast("Wow, this is a nice summit!")
    // Snackbars
    snackbar(view, "Hey, I'm a snackbar!")
    snackbar(view, R.string.message)
    snackbar(view, "Action!", "Click me!") { doYourStuff() }
    longSnackbar(view, "Wow, this is a nice summit!")
    MainActivity.kt

    View full-size slide

  52. Kotlin Android Extensions (kotlinx)

    View full-size slide

  53. O Kotlin Android Extensions (ou kotlinx) ajuda a evitar a necessidade de se
    utilizar o findViewById(…) na hora de se instanciar Views no Android
    (além de outras coisinhas a mais):

    View full-size slide

  54. Kotlin Android Extensions - Documentação Oficial
    kotlinlang.org/docs/tutorials/android-plugin.html

    View full-size slide

  55. apply plugin: "kotlin-android-extensions"
    app/build.gradle

    View full-size slide

  56. // Using R.layout.activity_main from the 'main' source set
    import kotlinx.android.synthetic.main.activity_main.*
    class MainActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    // Instead of findViewById(R.id.textView)
    textView.setText("Hello, Kotlin Community Summit!”)
    }
    }
    MainActivity.kt

    View full-size slide

  57. Também é possível utilizar o annotation
    @Parcelize para a criação de Parcelables:

    View full-size slide

  58. import kotlinx.android.parcel.Parcelize
    @Parcelize
    class User(val firstName: String,
    val lastName: String,
    val age: Int): Parcelable
    User.kt

    View full-size slide

  59. O kotlinx tem mais opções como Parcelers custom,
    cache de view (com políticas de cache) e outras coisas legais!

    View full-size slide

  60. Coroutines no Kotlin são threads "mais leves”, com uma sintaxe de código
    assíncrono tão direta quanto a de um código síncrono, com o objetivo
    de prover APIs simples para descomplicar operações assíncronas em geral.

    View full-size slide

  61. dependencies {
    implementation “org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version”
    implementation “org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
    implementation “org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
    }
    build.gradle

    View full-size slide

  62. kotlin {
    experimental {
    coroutines "enable"
    }
    }
    app/build.gradle

    View full-size slide

  63. Elas são tipos de funções chamadas suspending functions, e tem seus métodos
    marcados com a palavra chave suspend:

    View full-size slide

  64. // run the code on a background thread pool
    fun asyncOverlay() = async(CommonPool) {
    // start two async operations
    val original = asyncLoadImage("original")
    val overlay = asyncLoadImage(“overlay")
    // apply the overlay with both callbacks
    applyOverlay(original.await(), overlay.await())
    }
    // execute the coroutine on UI context
    launch(UI) {
    val image = asyncOverlay().await()
    showImage(image)
    }

    View full-size slide

  65. O launch é a maneira mais simples de se criar uma coroutine numa thread em
    background, tendo um Job (uma task, basicamente) como sua referência para si.

    View full-size slide

  66. public actual fun launch(
    context: CoroutineContext = DefaultDispatcher,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    parent: Job? = null,
    block: suspend CoroutineScope.() -> Unit
    ): Job

    View full-size slide

  67. Conceitualmente, o async é similar ao launch,
    mas a diferença deles é que o async retorna um Deferred,
    que é basicamente uma future (ou promise).

    View full-size slide

  68. public actual fun async(
    context: CoroutineContext = DefaultDispatcher,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    parent: Job? = null,
    block: suspend CoroutineScope.() -> T
    ): Deferred

    View full-size slide

  69. O CoroutineContext diz respeito a qual thread ela irá rodar: background
    (CommonPool) ou UI.

    View full-size slide

  70. Tecnicamente, um Deferred é um Job, só que com uma future com uma
    promessa de entregar um valor em breve.

    View full-size slide

  71. Ou seja: No async você tem a mesma funcionalidade do launch, mas com
    a diferença que ele retorna um valor no .await():

    View full-size slide

  72. fun getUser(userId: String): User = async {
    // return User
    }
    launch(UI) {
    val user = getUser(id).await()
    navigateToUserDetail(user)
    }

    View full-size slide

  73. Também é possível rodar alguma operação bloqueando
    completamente a thread, utilizando o runBlocking:

    View full-size slide

  74. runBlocking {
    delay(2000)
    }

    View full-size slide

  75. Também é possível cancelar um Job (ou um Deferred)
    e checar o estado dele, se eu quiser:

    View full-size slide

  76. val job = launch {
    // do your stuff
    }
    job.cancel()
    val job = launch {
    while (isActive){
    //do your stuff
    } }

    View full-size slide

  77. Resumindo:
    Se eu não preciso de um callback explícito -> launch
    Se eu preciso esperar uma future (Deferred) -> async
    Se eu preciso bloquear minha thread -> runBlocking

    View full-size slide

  78. Retrofit 2 Coroutine Adapter (Experimental) - Jake Wharton (GitHub)
    github.com/JakeWharton/retrofit2-kotlin-coroutines-adapter

    View full-size slide

  79. val retrofit = Retrofit.Builder()
    .baseUrl("https://example.com/")
    .addCallAdapterFactory(CoroutineCallAdapterFactory())
    .build()
    ...
    interface MyService {
    @GET("/user")
    fun getUser(): Deferred
    // or
    @GET("/user")
    fun getUser(): Deferred>
    }

    View full-size slide

  80. O Spek é um framework para especificação de testes para TDD
    (Test Driven Development) ou BDD (Behavior Driven Development).

    View full-size slide

  81. Spek - GitHub
    github.com/spekframework/spek

    View full-size slide

  82. apply plugin: "de.mannodermaus.android-junit5"
    app/build.gradle

    View full-size slide

  83. dependencies {
    testImplementation “org.spekframework.spek2:spek-dsl-jvm:$spek_version”
    testImplementation “org.spekframework.spek2:spek-runner-junit5:$spek_version”
    testImplementation “org.jetbrains.kotlin:kotlin-reflect:$spek_version”
    }
    app/build.gradle

    View full-size slide

  84. object MyTest: Spek({
    group("a group") {
    test("a test") {
    // your assertions here
    }
    group("a nested group") {
    test("another test") {
    // your assertions here
    }
    }
    }
    })
    MyTest.kt

    View full-size slide

  85. object MyTest: Spek({
    print("this is the root”)
    group("some group") {
    print("some group”)
    it("some test") {
    print("some test")
    }
    }
    group("another group") {
    print("another group”)
    it("another test") {
    print("another test")
    }
    }
    })
    MyTest.kt

    View full-size slide

  86. // this the root
    // - some group
    // - - another group
    // - some test
    // - - another test

    View full-size slide

  87. object MyTest: Spek({
    beforeGroup {
    print("before root")
    }
    group("some group") {
    beforeEachTest {
    print("before each test")
    }
    it("some test") {
    print("some test")
    }
    afterEachTest {
    print("before each test")
    }
    }
    afterGroup {
    print("after root")
    }
    })
    MyTest.kt

    View full-size slide

  88. // Instead of this:
    lateinit var calculator: Calculator
    beforeEachTest {
    calculator = Calculator()
    }
    // You can do:
    val calculator by memoized { Calculator() }

    View full-size slide

  89. object CalculatorSpec: Spek({
    describe("A calculator") {
    val calculator by memoized { Calculator() }
    describe("addition") {
    it("returns the sum of its arguments") {
    assertThat(3, calculator.add(1, 2))
    }
    }
    }
    })
    CalculatorSpec.kt

    View full-size slide

  90. O MockK é um biblioteca de mock otimizada para Kotlin e com diversas
    features interessantes como DSLs, suporte a annotations,
    argument capturing e outras.

    View full-size slide

  91. MockK - GitHub
    github.com/mockk/mockk

    View full-size slide

  92. dependencies {
    testImplementation “io.mockk:mockk:$mockk_version”
    }
    build.gradle.kts

    View full-size slide

  93. val car = mockk()
    every { car.drive(Direction.NORTH) } returns Outcome.OK
    car.drive(Direction.NORTH) // returns OK
    verify { car.drive(Direction.NORTH) }

    View full-size slide

  94. @ExtendWith(MockKExtension::class)
    class CarTest {
    @MockK
    lateinit var car1: Car
    @RelaxedMockK
    lateinit var car2: Car
    @MockK(relaxUnitFun = true)
    lateinit var car3: Car
    @SpyK
    var car4 = Car()
    @Test
    fun calculateAddsValues1() {
    // ... use car1, car2, car3 and car4
    }
    }

    View full-size slide

  95. Resumindo…

    View full-size slide

  96. Você precisa usar tudo isso no seu projeto? Definitivamente não!

    View full-size slide

  97. Mais importante do que sair utilizando todas essas bibliotecas
    é realmente entender como elas funcionam, como são construídas
    e tentar levar as boas ideias para sua rotina. "

    View full-size slide

  98. Links úteis

    View full-size slide

  99. Android KTX - GitHub
    github.com/android/android-ktx

    View full-size slide

  100. Detekt - GitHub
    github.com/arturbosch/detekt

    View full-size slide

  101. Gradle Kotlin DSL - GitHub
    github.com/gradle/kotlin-dsl

    View full-size slide

  102. Anko - GitHub
    github.com/kotlin/anko/

    View full-size slide

  103. Kotlin Android Extensions - Documentação Oficial
    kotlinlang.org/docs/tutorials/android-plugin.html

    View full-size slide

  104. Kotlin Coroutines - Documentação Oficial
    kotlinlang.org/docs/reference/coroutines.html

    View full-size slide

  105. Spek - GitHub
    github.com/spekframework/spek

    View full-size slide

  106. MockK - GitHub
    github.com/mockk/mockk

    View full-size slide

  107. Kotlin Meetup São Paulo
    meetup.com/kotlin-meetup-sp

    View full-size slide

  108. Android Dev BR - Slack
    slack.androiddevbr.org

    View full-size slide

  109. Podcast - Android Dev BR Cast
    bit.do/androiddevbrcast

    View full-size slide

  110. Vagas - Loggi
    bit.do/vempraloggi

    View full-size slide

  111. Muito obrigado! ♥

    View full-size slide

  112. Créditos de imagens:
    Fleur Treurniet (Unsplash) - bit.ly/2M5G4g6

    Google
    JetBrains
    GitHub

    View full-size slide