K O T L I N F O R A N D R O I D
D E V E L O P E R S
H A S S A N A B I D
C O N T E N T
• Why Kotlin
• Kotlin 101
• Kotlin vs. Java
• Advance Kotlin Topics
• Q & A
K O T L I N O N A N D R O I D S I N C E 2 0 1 7
K O T L I N 1 0 1
• It’s developed by JetBrains, and the fact that these are
the people behind a suite of IDEs, such as IntelliJ
• It’s pragmatic and concise, and makes coding a
satisfying and efficient experience.
• Kotlin is 100% interoperable with Java
S Y N TA X ( C L A S S )
class Foo {
val b: String = "b" // val means unmodifiable
var i: Int = 0 // var means modifiable
fun hello() {
val str = "Hello"
print("$str World")
}
fun sum(x: Int, y: Int): Int {
return x + y
}
fun maxOf(a: Float, b: Float) = if (a > b) a else b
}
S I N G L E L I N E
F U N C T I O N
S T R I N G I N T E R P O L AT I O N
// Java’s String.format() was built into the language
val x = 4
val y = 7
print("sum of $x and $y is ${x + y}") // sum of 4 and 7 is 11
T Y P E I N F E R E N C E
val a = "abc" // type inferred to String
val b = 4 // type inferred to Int
val c: Double = 0.7 // type declared explicitly
val d: List<String> = ArrayList() // type declared explicitly
S M A R T C A S T S
/* The Kotlin compiler tracks your logic and auto-casts types
if possible, which means no more instanceof checks
followed by explicit casts
*/
if (obj is String) {
print(obj.toUpperCase()) // obj is now known to be a String
}
I N T U I T I V E E Q U A L S
// no need to call equals()
val john1 = Person("John")
val john2 = Person("John")
john1 == john2 // true (structural equality)
john1 === john2 // false (referential equality)
D E FA U LT A R G U M E N T S
fun build(title: String, width: Int = 800, height: Int = 600) {
Frame(title, width, height)
}
N A M E D A R G U M E N T S
build("PacMan", 400, 300) // equivalent
build(title = "PacMan", width = 400, height = 300) // equivalent
build(width = 400, height = 300, title = "PacMan") // equivalent
W H E N E X P R E S S I O N ( S W I T C H )
when (x) {
1 -> print("x is 1")
2 -> print("x is 2")
3, 4 -> print("x is 3 or 4")
in 5..10 -> print("x is 5, 6, 7, 8, 9, or 10")
else -> print("x is out of range")
}
val res: Boolean = when {
obj == null -> false
obj is String -> true
else -> throw IllegalStateException()
}
P R O P E R T I E S
/*
Custom set & get behavior can be added to public fields,
which means we can stopbloating our code with mindless
getters & setters.
*/
class Frame {
var width: Int = 800
var height: Int = 600
val pixels: Int
get() = width * height
}
D E S T R U C T U R I N G D E C L A R AT I O N S
for ((key, value) in map) {
print("Key: $key")
print("Value: $value")
}
R A N G E S
for (i in 1..100) { ... }
for (i in 0 until 100) { ... }
for (i in 2..10 step 2) { ... }
for (i in 10 downTo 1) { ... }
if (x in 1..10) { ... }
E X T E N S I O N F U N C T I O N S
fun String.replaceSpaces(): String {
return this.replace(' ', '_')
}
val formatted = str.replaceSpaces()
N U L L S A F E T Y
/* Java is what we should call an almost statically typed language.
In it, a variable of type String is not guaranteed to
refer to a String— it might refer to null
*/
// Java developers have to live in constant fear of NPEs.
/* Kotlin resolves this by distinguishing between non-null
types and nullable types. Types are non-null by default,
and can be made nullable by adding a ? like so:
*/
var a: String = "abc"
a = null // compile error
var b: String? = "xyz"
b = null // no problem
N U L L S A F E T Y
//Kotlin forces you to guard against NPEs whenever
// you access a nullable type:
val x = b.length // compile error: b might be null
if (b == null) return
val x = b.length // no problem
/* We could also use a safe call ?.,
which evaluates to null instead of throwing a NPE:
*/
val x = b?.length // type of x is nullable Int
B E T T E R L A M A D A S
val sum = { x: Int, y: Int -> x + y } // type: (Int, Int) -> Int
val res = sum(4,7) // res == 11
numbers.filter({ x -> x.isPrime() })
numbers.filter { x -> x.isPrime() }
numbers.filter { it.isPrime() }
B E T T E R L A M A D A S
persons
.filter { it.age >= 18 }
.sortedBy { it.name }
.map { it.email }
.forEach { print(it) }
B E T T E R L A M A D A S
verticalLayout {
padding = dip(30)
editText {
hint = “Name”
textSize = 24f
}
editText {
hint = “Password”
textSize = 24f
}
button(“Login”) {
textSize = 26f
}
}
S C O P I N G F U N C T I O N S
// 1. with
val person: Person = getPerson()
with(person) {
print(name)
print(age)
}
// 2. apply
val peter = Person().apply {
// only access properties in apply block!
name = "Peter"
age = 18
}
S C O P I N G F U N C T I O N S
// 3. let
getNullablePerson()?.let {
// only executed when not-null
promote(it)
}
// 4. run
val inserted: Boolean = run {
val person: Person = getPerson()
val personDao: PersonDao = getPersonDao()
personDao.insert(person)
}
fun printAge(person: Person) = person.run {
print(age)
}
S C O P I N G F U N C T I O N S
private fun insert(user: User) = SqlBuilder().apply {
append("INSERT INTO user (email, name, age) VALUES ")
append("(?", user.email)
append(",?", user.name)
append(",?)", user.age)
}.also {
print("Executing SQL update: $it.")
}.run {
jdbc.update(this) > 0
}
K O T L I N E X T E N S I O N S -
K T X
K O T L I N E X T E N S I O N S F O R A N D R O I D
• Makes Android development more concise, pleasant
and idiomatic
• Writing clean, readable code is a lot easier and a lot
more fun
K T X E X A M P L E
view.addOnLayoutChangeListener(object : View.OnLayoutChangeListener
{ override fun onLayoutChange(
view: View?,
left: Int,
top: Int,
right: Int,
bottom: Int,
oldLeft: Int,
oldTop: Int,
oldRight: Int,
oldBottom: Int
){
doSomething()
} })
K T X E X A M P L E
view.addOnLayoutChangeListener(object : View.OnLayoutChangeListener
{ override fun onLayoutChange(
view: View?,
left: Int,
top: Int,
right: Int,
bottom: Int,
oldLeft: Int,
oldTop: Int,
oldRight: Int,
oldBottom: Int
){
doSomething()
} })
// KTX
view.doOnNextLayout { doSomething() }
S I N G L E L I N E
F U N C T I O N
A D D K T X I N Y O U R P R O J E C T
repositories {
google()
}
dependencies {
implementation 'androidx.core:core-ktx:1.0.0' }
A D D K T X I N Y O U R P R O J E C T
repositories {
google()
}
dependencies {
implementation 'androidx.core:core-ktx:1.0.0' }
M O R E K T X L I B S
androidx.core:core-ktx
androidx.fragment:fragment-ktx
androidx.palette:palette-ktx
androidx.sqlite:sqlite-ktx
androidx.collection:collection-ktx
androidx.lifecycle:lifecycle-viewmodel-ktx
androidx.lifecycle:lifecycle-reactivestreams-ktx
android.arch.navigation:navigation-common-ktx
android.arch.navigation:navigation-fragment-ktx
android.arch.navigation:navigation-runtime-ktx
android.arch.navigation:navigation-testing-ktx
android.arch.navigation:navigation-ui-ktx
android.arch.work:work-runtime-ktx
N A M E D PA R A M S A N D D E FA U LT VA L U E S
// Original drawable.setBounds(drawable.bounds.left,
drawable.bounds.top, drawable.bounds.right, 100)
// KTX
drawable.updateBounds(bottom = 100)
N A M E D PA R A M S A N D D E FA U LT VA L U E S
// Original
animator.addListener(object : Animator.AnimatorListener {
override fun onAnimationEnd(a: Animator?) { ... }
override fun onAnimationStart(a: Animator?) { }
override fun onAnimationCancel(a: Animator?) { }
override fun onAnimationRepeat(a: Animator?) { }
})
// KTX
animator.addListener(onEnd = { ... })
animator.doOnEnd { ... }
O P E R AT O R O V E R L O A D I N G
// Original
bitmap.getPixel(100, 100)
// KTX
bitmap[100, 100]
O P E R AT O R O V E R L O A D I N G
// Original
menu.removeItem(menuItem.itemId)
// KTX
menu -= menuItem
D E S T R U C T U R I N G
// Original
val lat = location.latitude
val long = location.longitude
// KTX
val (lat, long) = location
M O R E K T X E X A M P L E S
// Original
viewGroup.childCount != 0
// KTX
viewGroup.isEmpty() viewGroup.isNotEmpty()
// Original
for (index in 0 until viewGroup.childCount)
{ doSomething(viewGroup.getChildAt(index))
}x
// KTX
viewGroup.forEach { doSomething() }
M O R E K T X E X A M P L E S
// Original sharedPrefs.edit()
.putBoolean(key, valuex) .apply()
// KTX
sharedPrefs.edit { putBoolean(key, value) }
// Original
val builder = SpannableStringBuilder()
val start = builder.length builder.append("Hello") builder.setSpan(
StyleSpan(Typeface.BOLD),
start,
builder.length, Spanned.SPAN_INCLUSIVE_EXCLUSIVE
)
// KTX
builder.bold { append("Hello") }
builder.bold { italic { underline { append("Hello") } } }
K O T L I N C O R O U N T I N E S
C O R O U T I N E S
• Light-weight threads (for async programming)
• Alternative to Rx-Java
S I M P L E A P P A R C H I T E C T U R E
C L E A N A R C H I T E C T U R E
O P E N S O U R C E C O D E
• https://github.com/nickbutcher/plaid
• https://github.com/googlesamples/android-sunflower
• https://github.com/bufferapp/android-clean-
architecture-boilerplate
L E A R N I N G M AT E R I A L
• Udacity
• https://www.udacity.com/course/developing-
android-apps-with-kotlin--ud9012
Q U E S T I O N S ?
T H A N K Y O U

Kotlin for Android Developers

  • 1.
    K O TL I N F O R A N D R O I D D E V E L O P E R S H A S S A N A B I D
  • 2.
    C O NT E N T • Why Kotlin • Kotlin 101 • Kotlin vs. Java • Advance Kotlin Topics • Q & A
  • 4.
    K O TL I N O N A N D R O I D S I N C E 2 0 1 7
  • 5.
    K O TL I N 1 0 1 • It’s developed by JetBrains, and the fact that these are the people behind a suite of IDEs, such as IntelliJ • It’s pragmatic and concise, and makes coding a satisfying and efficient experience. • Kotlin is 100% interoperable with Java
  • 6.
    S Y NTA X ( C L A S S ) class Foo { val b: String = "b" // val means unmodifiable var i: Int = 0 // var means modifiable fun hello() { val str = "Hello" print("$str World") } fun sum(x: Int, y: Int): Int { return x + y } fun maxOf(a: Float, b: Float) = if (a > b) a else b } S I N G L E L I N E F U N C T I O N
  • 7.
    S T RI N G I N T E R P O L AT I O N // Java’s String.format() was built into the language val x = 4 val y = 7 print("sum of $x and $y is ${x + y}") // sum of 4 and 7 is 11
  • 8.
    T Y PE I N F E R E N C E val a = "abc" // type inferred to String val b = 4 // type inferred to Int val c: Double = 0.7 // type declared explicitly val d: List<String> = ArrayList() // type declared explicitly
  • 9.
    S M AR T C A S T S /* The Kotlin compiler tracks your logic and auto-casts types if possible, which means no more instanceof checks followed by explicit casts */ if (obj is String) { print(obj.toUpperCase()) // obj is now known to be a String }
  • 10.
    I N TU I T I V E E Q U A L S // no need to call equals() val john1 = Person("John") val john2 = Person("John") john1 == john2 // true (structural equality) john1 === john2 // false (referential equality)
  • 11.
    D E FAU LT A R G U M E N T S fun build(title: String, width: Int = 800, height: Int = 600) { Frame(title, width, height) }
  • 12.
    N A ME D A R G U M E N T S build("PacMan", 400, 300) // equivalent build(title = "PacMan", width = 400, height = 300) // equivalent build(width = 400, height = 300, title = "PacMan") // equivalent
  • 13.
    W H EN E X P R E S S I O N ( S W I T C H ) when (x) { 1 -> print("x is 1") 2 -> print("x is 2") 3, 4 -> print("x is 3 or 4") in 5..10 -> print("x is 5, 6, 7, 8, 9, or 10") else -> print("x is out of range") } val res: Boolean = when { obj == null -> false obj is String -> true else -> throw IllegalStateException() }
  • 14.
    P R OP E R T I E S /* Custom set & get behavior can be added to public fields, which means we can stopbloating our code with mindless getters & setters. */ class Frame { var width: Int = 800 var height: Int = 600 val pixels: Int get() = width * height }
  • 15.
    D E ST R U C T U R I N G D E C L A R AT I O N S for ((key, value) in map) { print("Key: $key") print("Value: $value") }
  • 16.
    R A NG E S for (i in 1..100) { ... } for (i in 0 until 100) { ... } for (i in 2..10 step 2) { ... } for (i in 10 downTo 1) { ... } if (x in 1..10) { ... }
  • 17.
    E X TE N S I O N F U N C T I O N S fun String.replaceSpaces(): String { return this.replace(' ', '_') } val formatted = str.replaceSpaces()
  • 18.
    N U LL S A F E T Y /* Java is what we should call an almost statically typed language. In it, a variable of type String is not guaranteed to refer to a String— it might refer to null */ // Java developers have to live in constant fear of NPEs. /* Kotlin resolves this by distinguishing between non-null types and nullable types. Types are non-null by default, and can be made nullable by adding a ? like so: */ var a: String = "abc" a = null // compile error var b: String? = "xyz" b = null // no problem
  • 19.
    N U LL S A F E T Y //Kotlin forces you to guard against NPEs whenever // you access a nullable type: val x = b.length // compile error: b might be null if (b == null) return val x = b.length // no problem /* We could also use a safe call ?., which evaluates to null instead of throwing a NPE: */ val x = b?.length // type of x is nullable Int
  • 20.
    B E TT E R L A M A D A S val sum = { x: Int, y: Int -> x + y } // type: (Int, Int) -> Int val res = sum(4,7) // res == 11 numbers.filter({ x -> x.isPrime() }) numbers.filter { x -> x.isPrime() } numbers.filter { it.isPrime() }
  • 21.
    B E TT E R L A M A D A S persons .filter { it.age >= 18 } .sortedBy { it.name } .map { it.email } .forEach { print(it) }
  • 22.
    B E TT E R L A M A D A S verticalLayout { padding = dip(30) editText { hint = “Name” textSize = 24f } editText { hint = “Password” textSize = 24f } button(“Login”) { textSize = 26f } }
  • 23.
    S C OP I N G F U N C T I O N S // 1. with val person: Person = getPerson() with(person) { print(name) print(age) } // 2. apply val peter = Person().apply { // only access properties in apply block! name = "Peter" age = 18 }
  • 24.
    S C OP I N G F U N C T I O N S // 3. let getNullablePerson()?.let { // only executed when not-null promote(it) } // 4. run val inserted: Boolean = run { val person: Person = getPerson() val personDao: PersonDao = getPersonDao() personDao.insert(person) } fun printAge(person: Person) = person.run { print(age) }
  • 25.
    S C OP I N G F U N C T I O N S private fun insert(user: User) = SqlBuilder().apply { append("INSERT INTO user (email, name, age) VALUES ") append("(?", user.email) append(",?", user.name) append(",?)", user.age) }.also { print("Executing SQL update: $it.") }.run { jdbc.update(this) > 0 }
  • 27.
    K O TL I N E X T E N S I O N S - K T X
  • 28.
    K O TL I N E X T E N S I O N S F O R A N D R O I D • Makes Android development more concise, pleasant and idiomatic • Writing clean, readable code is a lot easier and a lot more fun
  • 29.
    K T XE X A M P L E view.addOnLayoutChangeListener(object : View.OnLayoutChangeListener { override fun onLayoutChange( view: View?, left: Int, top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int ){ doSomething() } })
  • 30.
    K T XE X A M P L E view.addOnLayoutChangeListener(object : View.OnLayoutChangeListener { override fun onLayoutChange( view: View?, left: Int, top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int ){ doSomething() } }) // KTX view.doOnNextLayout { doSomething() } S I N G L E L I N E F U N C T I O N
  • 31.
    A D DK T X I N Y O U R P R O J E C T repositories { google() } dependencies { implementation 'androidx.core:core-ktx:1.0.0' }
  • 32.
    A D DK T X I N Y O U R P R O J E C T repositories { google() } dependencies { implementation 'androidx.core:core-ktx:1.0.0' }
  • 33.
    M O RE K T X L I B S androidx.core:core-ktx androidx.fragment:fragment-ktx androidx.palette:palette-ktx androidx.sqlite:sqlite-ktx androidx.collection:collection-ktx androidx.lifecycle:lifecycle-viewmodel-ktx androidx.lifecycle:lifecycle-reactivestreams-ktx android.arch.navigation:navigation-common-ktx android.arch.navigation:navigation-fragment-ktx android.arch.navigation:navigation-runtime-ktx android.arch.navigation:navigation-testing-ktx android.arch.navigation:navigation-ui-ktx android.arch.work:work-runtime-ktx
  • 34.
    N A ME D PA R A M S A N D D E FA U LT VA L U E S // Original drawable.setBounds(drawable.bounds.left, drawable.bounds.top, drawable.bounds.right, 100) // KTX drawable.updateBounds(bottom = 100)
  • 35.
    N A ME D PA R A M S A N D D E FA U LT VA L U E S // Original animator.addListener(object : Animator.AnimatorListener { override fun onAnimationEnd(a: Animator?) { ... } override fun onAnimationStart(a: Animator?) { } override fun onAnimationCancel(a: Animator?) { } override fun onAnimationRepeat(a: Animator?) { } }) // KTX animator.addListener(onEnd = { ... }) animator.doOnEnd { ... }
  • 36.
    O P ER AT O R O V E R L O A D I N G // Original bitmap.getPixel(100, 100) // KTX bitmap[100, 100]
  • 37.
    O P ER AT O R O V E R L O A D I N G // Original menu.removeItem(menuItem.itemId) // KTX menu -= menuItem
  • 38.
    D E ST R U C T U R I N G // Original val lat = location.latitude val long = location.longitude // KTX val (lat, long) = location
  • 39.
    M O RE K T X E X A M P L E S // Original viewGroup.childCount != 0 // KTX viewGroup.isEmpty() viewGroup.isNotEmpty() // Original for (index in 0 until viewGroup.childCount) { doSomething(viewGroup.getChildAt(index)) }x // KTX viewGroup.forEach { doSomething() }
  • 40.
    M O RE K T X E X A M P L E S // Original sharedPrefs.edit() .putBoolean(key, valuex) .apply() // KTX sharedPrefs.edit { putBoolean(key, value) } // Original val builder = SpannableStringBuilder() val start = builder.length builder.append("Hello") builder.setSpan( StyleSpan(Typeface.BOLD), start, builder.length, Spanned.SPAN_INCLUSIVE_EXCLUSIVE ) // KTX builder.bold { append("Hello") } builder.bold { italic { underline { append("Hello") } } }
  • 41.
    K O TL I N C O R O U N T I N E S
  • 42.
    C O RO U T I N E S • Light-weight threads (for async programming) • Alternative to Rx-Java
  • 43.
    S I MP L E A P P A R C H I T E C T U R E
  • 44.
    C L EA N A R C H I T E C T U R E
  • 45.
    O P EN S O U R C E C O D E • https://github.com/nickbutcher/plaid • https://github.com/googlesamples/android-sunflower • https://github.com/bufferapp/android-clean- architecture-boilerplate
  • 46.
    L E AR N I N G M AT E R I A L • Udacity • https://www.udacity.com/course/developing- android-apps-with-kotlin--ud9012
  • 47.
    Q U ES T I O N S ?
  • 48.
    T H AN K Y O U