Advanced Kotlin for Android #DroidKaigi #DroidKaigiB

Advanced Kotlin for Android #DroidKaigi #DroidKaigiB

DroidKaigi( https://droidkaigi.github.io/2016/ )で発表したスライドです。

14c9795d267f5b85abb98ca5e8780646?s=128

Taro Nagasawa

February 19, 2016
Tweet

Transcript

  1. 2.

    • 長澤太郎 - Taro Nagasawa • Twitter: @ngsw_taro • Programmer

    at M3, Inc. • Kotlin evangelist (unofficial) • Japan Kotlin User Group About me
  2. 4.

    Agenda • What is Kotlin • How to use Kotlin

    in Android projects • Real world Android Kotlin • Aggressive Kotlin • Summary
  3. 5.

    Agenda • What is Kotlin • How to use Kotlin

    in Android projects • Real world Android Kotlin • Aggressive Kotlin • Summary interest you in Kotlin
  4. 6.

    Agenda • What is Kotlin • How to use Kotlin

    in Android projects • Real world Android Kotlin • Aggressive Kotlin • Summary show tips and ideas
  5. 8.

    Kotlin • target JVM, JavaScript, Android • developed by JetBrains

    • Statically typed • Object-oriented • Modern grammar & features • The so-called “Better Java”
  6. 9.
  7. 10.

    Like this package sample fun main(args: Array<String>) { "world".hello() }

    fun String.hello() { println("Hello, $this") } top level function
  8. 11.

    Like this package sample fun main(args: Array<String>) { "world".hello() }

    fun String.hello() { println("Hello, $this") } extension function
  9. 12.

    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 } }
  10. 13.

    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
  11. 14.

    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
  12. 15.

    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
  13. 16.

    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
  14. 17.

    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
  15. 20.

    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
  16. 24.

    Finish • rename a directory “java” to “kotlin” • options

    ◦ specify Kotlin version in gradle.properties ◦ make project root depend on Kotlin plugin
  17. 26.

    JSR269 Annotation processing • for generating “Java” code by annotations

    in compilation time • Useful libraries ◦ Dagger ◦ Realm ◦ Butter Knife ◦ etc...
  18. 28.

    RealmObject open class User( open var id: Long = 0,

    @Required open var name: String = "" ): RealmObject()
  19. 29.

    RealmObject open class User( open var id: Long = 0,

    @Required open var name: String = "" ): RealmObject() provide setter/getter provide default constructor provide default constructor
  20. 33.

    (Example) Toast fun Context.toast(message: String, duration: Int = Toast.LENGTH_SHORT) {

    Toast.makeText(this, message, duration).show() } // in Activity toast("Hello")
  21. 34.

    (Example) Application object val Activity.app: MyApp get() = application as

    MyApp // in Activity app.component.inject(this)
  22. 35.

    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.
  23. 38.

    (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) } } } }
  24. 40.

    delegate other objects to property access class Greeter { val

    message: String by lazy { "Hello" } } lazy init
  25. 41.

    (Example) Extra fun Activity.stringExtra(name: String): Lazy<String> = 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) } }
  26. 42.

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

    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
  28. 45.

    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 }
  29. 46.

    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 (´;ω;`)
  30. 47.

    Use if expression if (foo != null && bar !=

    null && baz != null) { execute(foo, bar, baz) }
  31. 48.

    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
  32. 49.

    Answer 1: early returns fun method() { val foo =

    foo ?: return val bar = bar ?: return val baz = baz ?: return execute(foo, bar, baz) }
  33. 50.

    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!!!") }
  34. 56.

    want to use a ActivityLifecycleProvider’s method, but how ? //

    BOF fun <T> Observable<T>.bindToLifecycle() = compose<T>(???) // EOF ActivityLifecycleProvider# bindToLifecycle
  35. 57.

    define a extension function in a interface // BOF interface

    RxLifecyclerFeature: ActivityLifecycleProvider { fun <T> Observable<T>.bindToLifecycle() = compose<T>(this@RxLifecyclerFeature.bindToLifecycle()) } // EOF
  36. 58.

    implemtns the interface class MyActivity: RxActivity(), RxLifecycleFeature { fun go()

    { apiClient.findArticle(id) .bindToLifecycle() .observeOn(AndroidSchedulers.main()) .subscribe { ... } } ...
  37. 62.

    define a extension function in a interface // BOF interface

    IntentFeature { fun Intent.startActivity() { ??? } } // EOF
  38. 63.

    Additionally, define a abstract method // BOF interface IntentFeature {

    fun startActivity(intent: Intent) fun Intent.startActivity() { startActivity(this) } } // EOF
  39. 64.

    implement the interface class MyActivity: Activity(), IntentFeature { fun go()

    { createIntent(this) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra("key", "value") .startActivity() } ...
  40. 66.

    Without interfaces version class MyActivity: Activity() { fun go() {

    createIntent(this) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra("key", "value") .startActivity() } ...
  41. 68.

    provide a extension property // BOF val Activity.startActivity: Intent.()->Unit get()

    = { intent -> startActivity(intent) } // EOF Activity’s property
  42. 69.

    provide a extension property // BOF val Activity.startActivity: Intent.()->Unit get()

    = { intent -> startActivity(intent) } // EOF Intent’s method Intent’s method
  43. 71.

    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
  44. 72.

    Usage class MyActivity: Activity() { fun go() { createIntent(this) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)

    .putExtra("key", "value") .startActivity() } ... Method invocation Method invocation
  45. 73.

    I named this pattern “double context extension” • Pros ◦

    expressive ◦ keep your code base simple • Cons ◦ Code completion dose not work
  46. 74.
  47. 75.

    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