Slide 1

Slide 1 text

Advanced Kotlin for Android DroidKaigi Feb. 19, 2016 Taro @ngsw_taro

Slide 2

Slide 2 text

● 長澤太郎 - Taro Nagasawa ● Twitter: @ngsw_taro ● Programmer at M3, Inc. ● Kotlin evangelist (unofficial) ● Japan Kotlin User Group About me

Slide 3

Slide 3 text

エムスリー株式会社 - M3, Inc. ● provide services for health care workers

Slide 4

Slide 4 text

Agenda ● What is Kotlin ● How to use Kotlin in Android projects ● Real world Android Kotlin ● Aggressive Kotlin ● Summary

Slide 5

Slide 5 text

Agenda ● What is Kotlin ● How to use Kotlin in Android projects ● Real world Android Kotlin ● Aggressive Kotlin ● Summary interest you in Kotlin

Slide 6

Slide 6 text

Agenda ● What is Kotlin ● How to use Kotlin in Android projects ● Real world Android Kotlin ● Aggressive Kotlin ● Summary show tips and ideas

Slide 7

Slide 7 text

What is Kotlin

Slide 8

Slide 8 text

Kotlin ● target JVM, JavaScript, Android ● developed by JetBrains ● Statically typed ● Object-oriented ● Modern grammar & features ● The so-called “Better Java”

Slide 9

Slide 9 text

Like this package sample fun main(args: Array) { "world".hello() } fun String.hello() { println("Hello, $this") }

Slide 10

Slide 10 text

Like this package sample fun main(args: Array) { "world".hello() } fun String.hello() { println("Hello, $this") } top level function

Slide 11

Slide 11 text

Like this package sample fun main(args: Array) { "world".hello() } fun String.hello() { println("Hello, $this") } extension function

Slide 12

Slide 12 text

Very easy data class User(val id: String, val name: String) fun loadUser(id: String) { apiClient.getUser(id) .observeOn(AndroidSchedulers.main()) .subscribe { user -> userNameView.text = user.name } }

Slide 13

Slide 13 text

Very easy data class User(val id: String, val name: String) fun loadUser(id: String) { apiClient.getUser(id) .observeOn(AndroidSchedulers.main()) .subscribe { user -> userNameView.text = user.name } } data class

Slide 14

Slide 14 text

Very easy data class User(val id: String, val name: String) fun loadUser(id: String) { apiClient.getUser(id) .observeOn(AndroidSchedulers.main()) .subscribe { user -> userNameView.text = user.name } } function literal

Slide 15

Slide 15 text

Very easy data class User(val id: String, val name: String) fun loadUser(id: String) { apiClient.getUser(id) .observeOn(AndroidSchedulers.main()) .subscribe { user -> userNameView.text = user.name } } property access

Slide 16

Slide 16 text

Very easy data class User(val id: String, val name: String) fun loadUser(id: String) { apiClient.getUser(id) .observeOn(AndroidSchedulers.main()) .subscribe { user -> userNameView.text = user.name } } like properties

Slide 17

Slide 17 text

The time has come!!! ● 1.0 was released only a few days ago ● Some companies started to use Kotlin ● I am writing a Kotlin book now ○ It will be released by this late summer

Slide 18

Slide 18 text

How to use Kotlin in Android projects

Slide 19

Slide 19 text

(1) start Kotlin in Android Projects

Slide 20

Slide 20 text

Kotlin plugin installation 1. launch Android Studio 2. open “Preferences” 3. click “Install JetBrains plugin…” 4. find “Kotlin” from the list, and click “Install plugin” 5. wait for installation to complete 6. restart AS to enable the plugin

Slide 21

Slide 21 text

convert MainActivity.java to Kotlin code Code menu

Slide 22

Slide 22 text

add config for Kotlin to build.gradle Tools menu

Slide 23

Slide 23 text

add config for Kotlin to build.gradle apply plugin: 'com.android.application' apply plugin: 'kotlin-android'

Slide 24

Slide 24 text

Finish ● rename a directory “java” to “kotlin” ● options ○ specify Kotlin version in gradle.properties ○ make project root depend on Kotlin plugin

Slide 25

Slide 25 text

(2) use Realm and Dagger

Slide 26

Slide 26 text

JSR269 Annotation processing ● for generating “Java” code by annotations in compilation time ● Useful libraries ○ Dagger ○ Realm ○ Butter Knife ○ etc...

Slide 27

Slide 27 text

kapt dependencies { // Realm compile "io.realm:realm-android:$version" kapt "io.realm:realm-android:$version" } kapt { generateStubs = true }

Slide 28

Slide 28 text

RealmObject open class User( open var id: Long = 0, @Required open var name: String = "" ): RealmObject()

Slide 29

Slide 29 text

RealmObject open class User( open var id: Long = 0, @Required open var name: String = "" ): RealmObject() provide setter/getter provide default constructor provide default constructor

Slide 30

Slide 30 text

Real world Android Kotlin

Slide 31

Slide 31 text

(1) Extensions

Slide 32

Slide 32 text

Extension function package sample fun main(args: Array) { "world".hello() } fun String.hello() { println("Hello, $this") }

Slide 33

Slide 33 text

(Example) Toast fun Context.toast(message: String, duration: Int = Toast.LENGTH_SHORT) { Toast.makeText(this, message, duration).show() } // in Activity toast("Hello")

Slide 34

Slide 34 text

(Example) Application object val Activity.app: MyApp get() = application as MyApp // in Activity app.component.inject(this)

Slide 35

Slide 35 text

Scope functions provided by the stdlib cf. Kotlin スコープ関数 用途まとめ goo.gl/Ec1Gpi name receiver parameters return let T block: (T)->R R run T block: T.()->R R apply T block: T.()->Unit T with - receiver: T, block: T.()->R R * T and R are any types.

Slide 36

Slide 36 text

(Example) execute Intent startActivity(createIntent(this) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) .putExtra("key", "value")) createIntent(this) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) .putExtra("key", "value") .let { startActivity(it) }

Slide 37

Slide 37 text

(Example) No longer need fluent interface createIntent(this).run { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) if(value != null) { putExtra("key", value) } startActivity(this) }

Slide 38

Slide 38 text

(Example) new a fragment with arguments class MyFragment: Fragment() { companion object { fun new(foo: Int, bar: Int): MyFragment = MyFragment().apply { arguments = Bundle().apply { putInt("foo", foo) putInt("bar", bar) } } } }

Slide 39

Slide 39 text

(2) Delegated properties

Slide 40

Slide 40 text

delegate other objects to property access class Greeter { val message: String by lazy { "Hello" } } lazy init

Slide 41

Slide 41 text

(Example) Extra fun Activity.stringExtra(name: String): Lazy = lazy { requireNotNull(intent.getStringExtra(name), {"missing"}) } class MyActivity: Activity() { val message: String by stringExtra("message") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Log.d("MyActivity", message) } }

Slide 42

Slide 42 text

Kotter Knife ● View binding library for Kotlin ● developed by Jake Wharton ● use delegated properties val submitButton: Button by bindView(R.id.submit) val nameTextView: TextView? by bindOptionalView(R.id.name)

Slide 43

Slide 43 text

(3) Nullable

Slide 44

Slide 44 text

Null-Safety ● A mechanism to eliminate NullPointerException val s1: String = null // NG val s2: String? = null // OK s2.toUpperCase() // NG if(s2 != null) s2.toUppserCase() // OK s2?.toUpperCase() // OK

Slide 45

Slide 45 text

let is map, flatMap, ifPresent for the Optional in Java fun reverse(s: String): String {...} val reverse: String? = str?.let { reverse(it) } fun findById(id: Long): User? {...} val user: User? = userId?.let { findById(it) } user?.let { println(it }

Slide 46

Slide 46 text

let is useful, but if here are multiple nullable... foo?.let { foo -> bar?.let { bar -> baz?.let { baz -> execute(foo, bar, baz) } } } deep nesting ugly (´;ω;`)

Slide 47

Slide 47 text

Use if expression if (foo != null && bar != null && baz != null) { execute(foo, bar, baz) }

Slide 48

Slide 48 text

When foo, bar, baz are “var” properies... if (foo != null && bar != null && baz != null) { // NG because they are stil nullable execute(foo, bar, baz) } for { foo <- foo bar <- bar baz <- baz } yield execute(foo, bar, baz) Kotlin dose not have such syntax

Slide 49

Slide 49 text

Answer 1: early returns fun method() { val foo = foo ?: return val bar = bar ?: return val baz = baz ?: return execute(foo, bar, baz) }

Slide 50

Slide 50 text

Answer 2: early returns + “run” fun method() { run { val foo = foo ?: return@run val bar = bar ?: return@run val baz = baz ?: return@run execute(foo, bar, baz) } Log.d(TAG, "done!!!") }

Slide 51

Slide 51 text

Aggressive Kotlin

Slide 52

Slide 52 text

(1) Extensions in a interface

Slide 53

Slide 53 text

RxAndroid + RxLifecycle apiClient.findArticle(id) .compose(bindToLifecycle()) .observeOn(AndroidSchedulers.main()) .subscribe { articleView.article = it }

Slide 54

Slide 54 text

RxAndroid + RxLifecycle apiClient.findArticle(id) .compose(bindToLifecycle()) .observeOn(AndroidSchedulers.main()) .subscribe { articleView.article = it } cannot be inferred

Slide 55

Slide 55 text

A extension function is needed apiClient.findArticle(id) .bindToLifecycle() .observeOn(AndroidSchedulers.main()) .subscribe { articleView.article = it }

Slide 56

Slide 56 text

want to use a ActivityLifecycleProvider’s method, but how ? // BOF fun Observable.bindToLifecycle() = compose(???) // EOF ActivityLifecycleProvider# bindToLifecycle

Slide 57

Slide 57 text

define a extension function in a interface // BOF interface RxLifecyclerFeature: ActivityLifecycleProvider { fun Observable.bindToLifecycle() = compose([email protected]()) } // EOF

Slide 58

Slide 58 text

implemtns the interface class MyActivity: RxActivity(), RxLifecycleFeature { fun go() { apiClient.findArticle(id) .bindToLifecycle() .observeOn(AndroidSchedulers.main()) .subscribe { ... } } ...

Slide 59

Slide 59 text

(2) Extensions in a interface which has abstract methods

Slide 60

Slide 60 text

Can such a function be defined? createIntent(this) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra("key", "value") .startActivity()

Slide 61

Slide 61 text

Can this function be defined? createIntent(this) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra("key", "value") .startActivity() Intent’s method AND use Activity’s method (i.e. startActivity)

Slide 62

Slide 62 text

define a extension function in a interface // BOF interface IntentFeature { fun Intent.startActivity() { ??? } } // EOF

Slide 63

Slide 63 text

Additionally, define a abstract method // BOF interface IntentFeature { fun startActivity(intent: Intent) fun Intent.startActivity() { startActivity(this) } } // EOF

Slide 64

Slide 64 text

implement the interface class MyActivity: Activity(), IntentFeature { fun go() { createIntent(this) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra("key", "value") .startActivity() } ...

Slide 65

Slide 65 text

(3) “Double context extensions”

Slide 66

Slide 66 text

Without interfaces version class MyActivity: Activity() { fun go() { createIntent(this) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra("key", "value") .startActivity() } ...

Slide 67

Slide 67 text

provide a extension property // BOF val Activity.startActivity: Intent.()->Unit get() = { intent -> startActivity(intent) } // EOF

Slide 68

Slide 68 text

provide a extension property // BOF val Activity.startActivity: Intent.()->Unit get() = { intent -> startActivity(intent) } // EOF Activity’s property

Slide 69

Slide 69 text

provide a extension property // BOF val Activity.startActivity: Intent.()->Unit get() = { intent -> startActivity(intent) } // EOF Intent’s method Intent’s method

Slide 70

Slide 70 text

Usage class MyActivity: Activity() { fun go() { createIntent(this) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra("key", "value") .startActivity() } ...

Slide 71

Slide 71 text

Usage class MyActivity: Activity() { fun go() { createIntent(this) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra("key", "value") .startActivity() } ... Activity’s property, which returns a Intent’method

Slide 72

Slide 72 text

Usage class MyActivity: Activity() { fun go() { createIntent(this) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra("key", "value") .startActivity() } ... Method invocation Method invocation

Slide 73

Slide 73 text

I named this pattern “double context extension” ● Pros ○ expressive ○ keep your code base simple ● Cons ○ Code completion dose not work

Slide 74

Slide 74 text

Summary

Slide 75

Slide 75 text

Summary ● Kotlin is a pragmatic language for Android ● The time has come! ● Getting started is easy ● Extensions & scope functions are your friends ● It’s easy to deal with nullable ● Interfaces make extensions more powerful ● “Double context extensions”, my idea

Slide 76

Slide 76 text

Thank you, Enjoy Kotlin