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/

B567f69eed037ed20536fc825305a38d?s=128

Walmyr Carvalho

September 15, 2018
Tweet

Transcript

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

    a ponta! +
  2. Walmyr Carvalho @walmyrcarvalho

  3. None
  4. Contexto

  5. Um fato: Kotlin veio pra ficar! ❤

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

  7. 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
  8. E como a gente pode usar Kotlin nas nossas aplicações?

  9. None
  10. None
  11. Será que podemos ir mais longe com Kotlin no Android?

  12. Oportunidades

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

  14. Geral UI Async Testes Build

  15. Kotlin + Android KTX Anko + Kotlin Android Extensions (kotlinx)

    Coroutines Spek + MockK Geral UI Async Testes Build Gradle Kotlin DSL + Detekt
  16. None
  17. Android KTX

  18. O Android KTX é um conjunto de extension functions criadas

    pelo Google para otimizar o desenvolvimento de tarefas comuns do Android.
  19. Android KTX - GitHub github.com/android/android-ktx

  20. 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
  21. repositories { google() } build.gradle

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

  23. // Kotlin db.beginTransaction() try { // insira os dados db.setTransactionSuccessful()

    } finally { db.endTransaction() } // Kotlin + Android KTX db.transaction { // insira os dados }
  24. Mas… como é a implementação disso?

  25. inline fun <T> 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
  26. // Kotlin sharedPreferences.edit() .putBoolean("key", value) .apply() // Kotlin + Android

    KTX sharedPreferences.edit { putBoolean("key", value) }
  27. // Kotlin view.viewTreeObserver.addOnPreDrawListener( object : ViewTreeObserver.OnPreDrawListener { override fun onPreDraw():

    Boolean { viewTreeObserver.removeOnPreDrawListener(this) actionToBeTriggered() return true } } ) // Kotlin + Android KTX view.doOnPreDraw { actionToBeTriggered() }
  28. context.withStyledAttributes(set = someAttributeSet, attrs = attributes, defStyleAttr = ..., defStyleRes

    = ...) { // do your rolê } context.withStyledAttributes(set = someAttributeSet, attrs = attributes) { // do your rolê }
  29. Gradle Kotlin DSL

  30. Sim, é isso mesmo que cê tá pensando: scripts .gradle

    escritos em Kotlin! ❤
  31. Gradle Kotlin DSL - GitHub github.com/gradle/kotlin-dsl

  32. Atenção: Para utilizar o Gradle Kotlin DSL, precisamos utilizar a

    versão 4.10 (ou superior) do Gradle.
  33. Para começar:

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

  35. 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
  36. import org.jetbrains.kotlin.config.KotlinCompilerVersion plugins { id("com.android.application") id("kotlin-android") id("kotlin-android-extensions") } app/build.gradle.kts

  37. 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
  38. 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
  39. None
  40. Gradle Goes Kotlin - Hans Dockter (CEO, Gradle) youtu.be/4gmanjWNZ8E

  41. Detekt

  42. 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.
  43. Detekt - GitHub github.com/arturbosch/detekt

  44. buildscript { repositories { jcenter() } } plugins { id("io.gitlab.arturbosch.detekt").version("[version]")

    } detekt { toolVersion = "[version]" input = files("src/main/kotlin") filters = ".*/resources/.*,.*/build/.*" } build.gradle
  45. None
  46. Os arquivos de configuração do Detekt utilizam yaml:

  47. 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
  48. 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
  49. 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
  50. 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
  51. None
  52. Anko

  53. 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).
  54. Anko - GitHub github.com/kotlin/anko/

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

  56. 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.
  57. 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
  58. 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
  59. linearLayout { button("Login") { textSize = 26f }.lparams(width = wrapContent)

    { horizontalMargin = dip(5) topMargin = dip(10) } } MainActivity.kt
  60. verticalLayout { editText { hint = "Name" } editText {

    hint = "Password" } }.applyRecursively { view -> when(view) { is EditText -> view.textSize = 20f }} MainActivity.kt
  61. 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.
  62. // 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
  63. None
  64. Kotlin Android Extensions (kotlinx)

  65. 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):
  66. Kotlin Android Extensions - Documentação Oficial kotlinlang.org/docs/tutorials/android-plugin.html

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

  68. // 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<TextView>(R.id.textView) textView.setText("Hello, Kotlin Community Summit!”) } } MainActivity.kt
  69. Também é possível utilizar o annotation @Parcelize para a criação

    de Parcelables:
  70. import kotlinx.android.parcel.Parcelize @Parcelize class User(val firstName: String, val lastName: String,

    val age: Int): Parcelable User.kt
  71. O kotlinx tem mais opções como Parcelers custom, cache de

    view (com políticas de cache) e outras coisas legais!
  72. Coroutines

  73. 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.
  74. 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

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

  76. Elas são tipos de funções chamadas suspending functions, e tem

    seus métodos marcados com a palavra chave suspend:
  77. // 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) }
  78. 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.
  79. public actual fun launch( context: CoroutineContext = DefaultDispatcher, start: CoroutineStart

    = CoroutineStart.DEFAULT, parent: Job? = null, block: suspend CoroutineScope.() -> Unit ): Job
  80. Conceitualmente, o async é similar ao launch, mas a diferença

    deles é que o async retorna um Deferred, que é basicamente uma future (ou promise).
  81. public actual fun <T> async( context: CoroutineContext = DefaultDispatcher, start:

    CoroutineStart = CoroutineStart.DEFAULT, parent: Job? = null, block: suspend CoroutineScope.() -> T ): Deferred<T>
  82. O CoroutineContext diz respeito a qual thread ela irá rodar:

    background (CommonPool) ou UI.
  83. Tecnicamente, um Deferred é um Job, só que com uma

    future com uma promessa de entregar um valor em breve.
  84. Ou seja: No async você tem a mesma funcionalidade do

    launch, mas com a diferença que ele retorna um valor no .await():
  85. fun getUser(userId: String): User = async { // return User

    } launch(UI) { val user = getUser(id).await() navigateToUserDetail(user) }
  86. Também é possível rodar alguma operação bloqueando completamente a thread,

    utilizando o runBlocking:
  87. runBlocking { delay(2000) }

  88. Também é possível cancelar um Job (ou um Deferred<T>) e

    checar o estado dele, se eu quiser:
  89. val job = launch { // do your stuff }

    job.cancel() val job = launch { while (isActive){ //do your stuff } }
  90. 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
  91. Retrofit 2 Coroutine Adapter (Experimental) - Jake Wharton (GitHub) github.com/JakeWharton/retrofit2-kotlin-coroutines-adapter

  92. val retrofit = Retrofit.Builder() .baseUrl("https://example.com/") .addCallAdapterFactory(CoroutineCallAdapterFactory()) .build() ... interface MyService

    { @GET("/user") fun getUser(): Deferred<User> // or @GET("/user") fun getUser(): Deferred<Response<User>> }
  93. None
  94. Spek

  95. O Spek é um framework para especificação de testes para

    TDD (Test Driven Development) ou BDD (Behavior Driven Development).
  96. Spek - GitHub github.com/spekframework/spek

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

  98. 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

  99. object MyTest: Spek({ group("a group") { test("a test") { //

    your assertions here } group("a nested group") { test("another test") { // your assertions here } } } }) MyTest.kt
  100. 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
  101. // this the root // - some group // -

    - another group // - some test // - - another test
  102. 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
  103. // Instead of this: lateinit var calculator: Calculator beforeEachTest {

    calculator = Calculator() } // You can do: val calculator by memoized { Calculator() }
  104. 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
  105. MockK

  106. O MockK é um biblioteca de mock otimizada para Kotlin

    e com diversas features interessantes como DSLs, suporte a annotations, argument capturing e outras.
  107. MockK - GitHub github.com/mockk/mockk

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

  109. val car = mockk<Car>() every { car.drive(Direction.NORTH) } returns Outcome.OK

    car.drive(Direction.NORTH) // returns OK verify { car.drive(Direction.NORTH) }
  110. @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 } }
  111. Resumindo…

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

  113. 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. "
  114. Dúvidas?

  115. Links úteis

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

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

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

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

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

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

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

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

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

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

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

  127. Vagas - Loggi bit.do/vempraloggi

  128. Muito obrigado! ♥

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

    GitHub