Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

Walmyr Carvalho @walmyrcarvalho

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Contexto

Slide 5

Slide 5 text

Um fato: Kotlin veio pra ficar! ❤

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

Oportunidades

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

Geral UI Async Testes Build

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

Android KTX

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

repositories { google() } build.gradle

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

Mas… como é a implementação disso?

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

Gradle Kotlin DSL

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

Para começar:

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

Detekt

Slide 42

Slide 42 text

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.

Slide 43

Slide 43 text

Detekt - GitHub github.com/arturbosch/detekt

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

No content

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

No content

Slide 52

Slide 52 text

Anko

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

Anko - GitHub github.com/kotlin/anko/

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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.

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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.

Slide 62

Slide 62 text

// 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

Slide 63

Slide 63 text

No content

Slide 64

Slide 64 text

Kotlin Android Extensions (kotlinx)

Slide 65

Slide 65 text

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):

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

// 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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

Coroutines

Slide 73

Slide 73 text

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.

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

// 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) }

Slide 78

Slide 78 text

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.

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

runBlocking { delay(2000) }

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

No content

Slide 94

Slide 94 text

Spek

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

Spek - GitHub github.com/spekframework/spek

Slide 97

Slide 97 text

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

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

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

Slide 102

Slide 102 text

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

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

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

Slide 105

Slide 105 text

MockK

Slide 106

Slide 106 text

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

Slide 107

Slide 107 text

MockK - GitHub github.com/mockk/mockk

Slide 108

Slide 108 text

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

Slide 109

Slide 109 text

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

Slide 110

Slide 110 text

@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 } }

Slide 111

Slide 111 text

Resumindo…

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

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

Slide 114

Slide 114 text

Dúvidas?

Slide 115

Slide 115 text

Links úteis

Slide 116

Slide 116 text

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

Slide 117

Slide 117 text

Detekt - GitHub github.com/arturbosch/detekt

Slide 118

Slide 118 text

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

Slide 119

Slide 119 text

Anko - GitHub github.com/kotlin/anko/

Slide 120

Slide 120 text

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

Slide 121

Slide 121 text

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

Slide 122

Slide 122 text

Spek - GitHub github.com/spekframework/spek

Slide 123

Slide 123 text

MockK - GitHub github.com/mockk/mockk

Slide 124

Slide 124 text

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

Slide 125

Slide 125 text

Android Dev BR - Slack slack.androiddevbr.org

Slide 126

Slide 126 text

Podcast - Android Dev BR Cast bit.do/androiddevbrcast

Slide 127

Slide 127 text

Vagas - Loggi bit.do/vempraloggi

Slide 128

Slide 128 text

Muito obrigado! ♥

Slide 129

Slide 129 text

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