Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Streamline your Android app API with Kotlin

Streamline your Android app API with Kotlin

Since May of 2017, Kotlin became officially acknowledged by Google and promoted to first class citizen when we talk about native Android application development.

Android framework is fully written in Java, which means that in its core there is a lot of APIs that are robust and not so easy/straightforward to use. In this talk, we'll go through tips and tricks how Kotlin can help us to streamline those API-s in our apps and make the code more readable and more easy to use.

Nebojša Vukšić

May 17, 2019
Tweet

More Decks by Nebojša Vukšić

Other Decks in Programming

Transcript

  1. SHORT OVERVIEW OF ANDROID HISTORY 3 2008
 Initial launch 2009

    2016 2010 2011 2012 2013 2014 2015 2017 2018
  2. 4 2008
 Initial launch 2016 2009 2010 2011 2012 2013

    2014 2015 2017 2018 SHORT OVERVIEW OF ANDROID HISTORY
  3. 5 2008 2016 2009 Cupcake
 Donut
 Eclair 2010 Froyo
 Gingerbread

    2011 Honeycomb 
 Ice Cream Sandwich 2012 Jelly Bean 2013 2014 2015 2017 2018 SHORT OVERVIEW OF ANDROID HISTORY
  4. 6 2008 2016 2009 2010 2011 2012 2013 KitKat 2014

    2015 2017 2018 SHORT OVERVIEW OF ANDROID HISTORY
  5. 7 2008 2016 2009 2010 2011 2012 2013 2014
 Lollipop

    2015 2017 2018 SHORT OVERVIEW OF ANDROID HISTORY
  6. 8 2008 2016 2009 2010 2011 2012 2013 2014
 Lollipop

    2015 2017 2018 SHORT OVERVIEW OF ANDROID HISTORY
  7. 9 2008 2016 2009 2010 2011 2012 2013 2014
 Lollipop

    2015 2017 2018 SHORT OVERVIEW OF ANDROID HISTORY
  8. We are stuck in Java 7 world (few Java 8

    features supported by platform) Android API ceremony Unable to extend Android platform types 10 Those backend devs are lucky ☹
  9. 11 2008 2016 2009 2010 2011 2012 2013 2014
 Lollipop

    2015 2017 2018 SHORT OVERVIEW OF ANDROID HISTORY
  10. 12 2008 2016 2009 2010 2011 2012 2013 2014
 Lollipop

    2015 2017 2018 SHORT OVERVIEW OF ANDROID HISTORY
  11. 13 2008 2016 2009 2010 2011 2012 2013 2014
 Lollipop

    2015 2017 2018 SHORT OVERVIEW OF ANDROID HISTORY
  12. 14 2008 2016 2009 2010 2011 2012 2013 2014
 Lollipop

    2015 2017 2018 7.5 SHORT OVERVIEW OF ANDROID HISTORY
  13. 15 2008 2016 2009 2010 2011 2012 2013 2014
 Lollipop

    2015 2017 2018 SHORT OVERVIEW OF ANDROID HISTORY
  14. 16 2008 2016 Nougat 2009 2010 2011 2012 2013 2014

    2015
 Marshmallow 2017 Oreo 2018 SHORT OVERVIEW OF ANDROID HISTORY
  15. 17 2008 2016 Nougat 2009 2010 2011 2012 2013 2014

    2015
 Marshmallow 2017 Oreo 2018 SHORT OVERVIEW OF ANDROID HISTORY
  16. 18

  17. JVM language interoperable with Java 1 Allows extensions of the

    platform types Provides higher order functions on collections Type safety built in type system Many other modern language features 2 3 4 5 Kotlin 19
  18. SHORT HISTORY OF ANDROID 20 2008 2016 Nougat 2009 2010

    2011 2012 2013 2014 2015
 Marshmallow 2017 Oreo 2018
  19. SHORT HISTORY OF ANDROID 21 2008 2016 Nougat 2009 2010

    2011 2012 2013 2014 2015
 Marshmallow 2017 Oreo 2018
  20. SHORT HISTORY OF ANDROID 22 2016 Nougat 2012 2013 2014


    Lollipop 2015
 Marshmallow 2017 Oreo 2018 2019
  21. 24

  22. 28

  23. 29

  24. if(screen.requiresSearchIcon) { searchIcon.visibility = View.VISIBLE } else { searchIcon.visibility =

    View.GONE } 31 class Screen(…, val requiresSearchIcon: Boolean, …)
  25. { } { } if(screen.requiresSearchIcon) { searchIcon.visibility = View.VISIBLE }

    else { searchIcon.visibility = View.GONE } 32 class Screen(…, val requiresSearchIcon: Boolean, …)
  26. 33 searchIcon.visibility = if(screen.requiresSearchIcon) { View.VISIBLE } else { View.GONE

    } class Screen(…, val requiresSearchIcon: Boolean, …)
  27. searchIcon.isVisible 34 class Screen(…, val requiresSearchIcon: Boolean, …) val View.isVisible:

    Boolean get() = visibility == View.VISIBLE set(isVisible) { visibility = if(isVisible) View.VISIBLE else View.GONE } searchIcon.visibility = if(screen.requiresSearchIcon) { View.VISIBLE } else { View.GONE }
  28. searchIcon.isVisible = screen.requiresSearchIcon class Screen(…, val requiresSearchIcon: Boolean, …) 35

    val View.isVisible: Boolean get() = visibility == View.VISIBLE set(isVisible) { visibility = if(isVisible) View.VISIBLE else View.GONE }
  29. inline fun View.updatePadding( left: Int = paddingLeft, top: Int =

    paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom) } sessionTitleView.setPadding( 10, message.paddingTop, 10, message.paddingBottom) 37
  30. inline fun View.updatePadding( left: Int = paddingLeft, top: Int =

    paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom) } inline fun View.updatePadding( left: Int = paddingLeft, top: Int = paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom) } sessionTitleView.setPadding( 10, message.paddingTop, 10, message.paddingBottom) 38
  31. inline fun View.updatePadding( left: Int = paddingLeft, top: Int =

    paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom) } sessionTitleView.updatePadding(left = 10, right = 10) 39
  32. inline fun View.updatePadding( left: Int = paddingLeft, top: Int =

    paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom) } sessionTitleView.updatePadding(left = 10, right = 10) 40 inline fun View.updatePadding( left: Int = paddingLeft, top: Int = paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom) }
  33. sessionTitleView.updatePadding(left = 10, right = 10) 41 Byte code: int

    leftPadding$iv = 10; int rightPadding$iv = 10; int topPadding$iv = $this$updatePadding$iv.getPaddingTop(); int bottomPadding$iv = $this$updatePadding$iv.getPaddingBottom(); $this$updatePadding$iv.setPadding( leftPadding$iv, topPadding$iv, rightPadding$iv, bottomPadding$iv);
  34. val viewModel = ViewModelProviders.of(this).get(SpeakersViewModel::class.java) 42 inline fun <reified T :

    ViewModel> FragmentActivity.getViewModel(): T = ViewModelProviders.of(this).get(T::class.java) val viewModel = this.getViewModel<SpeakersViewModel>()
  35. val viewModel = ViewModelProviders.of(this).get(SpeakersViewModel::class.java) 43 inline fun <reified T :

    ViewModel> FragmentActivity.getViewModel(): T = ViewModelProviders.of(this).get(T::class.java)
  36. 44 inline fun <reified T : ViewModel> FragmentActivity.getViewModel(): T =

    ViewModelProviders.of(this).get(T::class.java) SpeakersViewModel val viewModel = ViewModelProviders.of(this).get(SpeakersViewModel::class.java)
  37. 45 inline fun <reified T : ViewModel> FragmentActivity.getViewModel(): T =

    ViewModelProviders.of(this).get(T::class.java) val viewModel = getViewModel<SpeakersViewModel>()
  38. 50 inline fun Handler.postDelayed(delay: Long, crossinline block: () -> Unit)

    { postDelayed({ block() }, delay) } sessionTitle.handler.postDelayed({}, 300)
  39. 51 inline fun Handler.postDelayed(delay: Long, crossinline block: () -> Unit)

    { postDelayed({ block() }, delay) } sessionTitle.handler.postDelayed(300) { //does something }
  40. val color = Color.rgb(255, 64, 129) 52 val red =

    Color.red(color) val green = Color.green(color) val blue = Color.blue(color) val invColor = Color.rgb(255 - red, 255 - green, 255 - blue)
  41. val color = Color.rgb(255, 64, 129) 53 val red, =

    Color.red(color) val green, = Color.green(color) val blue, = Color.blue(color) val invColor = Color.rgb(255 - red, 255 - green, 255 - blue)
  42. val color = Color.rgb(255, 64, 129) 54 val red, =

    color.red val green, = color.green val blue, = color.blue val invColor = Color.rgb(255 - red, 255 - green, 255 - blue)
  43. 55 val red, = color.red val green, = color.green val

    blue, = color.blue inline val @receiver:ColorInt Int.red: Int get() = Color.red(this) inline val @receiver:ColorInt Int.green: Int get() = Color.green(this) inline val @receiver:ColorInt Int.blue: Int get() = Color.blue(this)
  44. val color = Color.rgb(255, 64, 129) 56 val red, =

    color.red val green, = color.green val blue, = color.blue val invColor = Color.rgb(255 - red, 255 - green, 255 - blue)
  45. val color = Color.rgb(255, 64, 129) 57 val red, =

    color.red val green, = color.green val blue, = color.blue val invColor = Color.rgb(255 - red, 255 - green, 255 - blue)
  46. val color = Color.rgb(255, 64, 129) 58 val (red, green,

    blue) = color val invColor = Color.rgb(255 - red, 255 - green, 255 - blue)
  47. val color = Color.rgb(255, 64, 129) 60 val (red, green,

    blue) = color val invColor = Color.rgb(255 - red, 255 - green, 255 - blue)
  48. 61 val (red, green, blue) = color inline operator fun

    @receiver:ColorInt Int.component1(): Int = this.red inline operator fun @receiver:ColorInt Int.component2(): Int = this.green inline operator fun @receiver:ColorInt Int.component3(): Int = this.blue
  49. speakerInputField.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(speakerName: Editable?) { adapter.filterByName(speakerName.toString())

    } override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { //does nothing } override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { //does nothing } }) speakerInputField.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(speakerName: Editable?) { adapter.filterByName(speakerName.toString()) } override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { //does nothing } override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { //does nothing } }) 62
  50. fun EditText.afterTextChanged(block:(String) -> Unit) { addTextChangeListener(object : TextWatcher { override

    fun afterTextChanged(text: Editable?) { block(text.toString()) } override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} }) } speakerInputField.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(speakerName: Editable?) { adapter.filterByName(speakerName.toString()) } … }) speakerInputField.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(speakerName: Editable?) { adapter.filterByName(speakerName.toString()) } … }) 64
  51. fun EditText.afterTextChanged(block:(String) -> Unit) { addTextChangeListener(object : TextWatcher { override

    fun afterTextChanged(text: Editable?) { block(text.toString()) } override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} }) } fun EditText.afterTextChanged(block:(String) -> Unit) { addTextChangeListener(object : TextWatcher { override fun afterTextChanged(text: Editable?) { block(text.toString()) } override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} }) } speakerInputField.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(speakerName: Editable?) { adapter.filterByName(speakerName.toString()) } … }) speakerInputField.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(speakerName: Editable?) { adapter.filterByName(speakerName.toString()) } … }) 65
  52. fun EditText.afterTextChanged(block:(String) -> Unit) { addTextChangeListener(object : TextWatcher { override

    fun afterTextChanged(text: Editable?) { block(text.toString()) } override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} }) } fun EditText.afterTextChanged(block:(String) -> Unit) { addTextChangeListener(object : TextWatcher { override fun afterTextChanged(text: Editable?) { block(text.toString()) } override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} }) } 66 speakerInputField.afterTextChanged { speakerName -> adapter.filterByName(speakerName) }
  53. 67 speakerInputField.afterTextChanged { speakerName -> adapter.filterByName(speakerName) } speakerInputField.beforeTextChanged { }

    speakerInputField.onTextChanged { } speakerInputField.addTextChangeListeners( afterTextChanged = { … }, onTextChanged = { … } )
  54. 68 fun EditText.addTextListeners( afterTextChanged: (String) -> Unit = {}, beforeTextChanged:

    (CharSequence?, Int, Int) -> Unit = {_,_,_ -> }, onTextChanged: (CharSequence?, Int, Int, Int) -> Unit = {_,_,_,_ -> }, ) { addTextChangeListener(object : TextWatcher { override fun afterTextChanged(text: Editable?) { afterTextChanged(text.toString() } override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { beforeTextChanged(s, start, count) } override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { onTextChanged(s, start, before, count) } }) }
  55. 69 animator.addListener(object : Animator.AnimatorListener { override fun onAnimationRepeat(animation: Animator?) {

    } override fun onAnimationEnd(animation: Animator?) { } override fun onAnimationCancel(animation: Animator?) { } override fun onAnimationStart(animation: Animator?) { } })
  56. 70 animator.addListener(object : Animator.AnimatorListener { override fun onAnimationRepeat(animation: Animator?) {

    } override fun onAnimationEnd(animation: Animator?) { } override fun onAnimationCancel(animation: Animator?) { } override fun onAnimationStart(animation: Animator?) { } }) animator.doOnRepeat { } animator.doOnEnd { } animator.doOnCancel { } animator.doOnStart { } animator.addListener(doOnEnd = { }, doOnCancel = { })
  57. 71 class SpeakersAdapter : RecyclerView.Adapter<SpeakersAdapter.ViewHolder>() { private val speakerList: MutableList<Speaker>

    = mutableListOf() override fun onCreateViewHolder(parent: ViewGroup, vt: Int): ViewHolder {...} override fun onBindViewHolder(vHolder: ViewHolder, position: Int) {...} override fun getItemCount(): Int {...} fun addAllSpeakers(speakers: List<Speaker>) { speakerList.clear() speakerList.addAll(speakers) notifyDataSetChanged() } }
  58. 72 class SpeakersAdapter : RecyclerView.Adapter<SpeakersAdapter.ViewHolder>() { private val speakerList: MutableList<Speaker>

    = mutableListOf() override fun onCreateViewHolder(parent: ViewGroup, vt: Int): ViewHolder {...} override fun onBindViewHolder(vHolder: ViewHolder, position: Int) {...} override fun getItemCount(): Int {...} fun addAllSpeakers(speakers: List<Speaker>) { speakerList.clear() speakerList.addAll(speakers) notifyDataSetChanged() } } class SpeakersAdapter : RecyclerView.Adapter<SpeakersAdapter.ViewHolder>() { private val speakerList: MutableList<Speaker> = mutableListOf() override fun onCreateViewHolder(parent: ViewGroup, vt: Int): ViewHolder {...} override fun onBindViewHolder(vHolder: ViewHolder, position: Int) {...} override fun getItemCount(): Int {...} fun addAllSpeakers(speakers: List<Speaker>) { speakerList.clear() speakerList.addAll(speakers) notifyDataSetChanged() } }
  59. 74 class SpeakersAdapter : RecyclerView.Adapter<SpeakersAdapter.ViewHolder>() { … fun addAllSpeakers(speakers: List<Speaker>)

    { speakerList.clear() speakerList.addAll(speakers) notifyDataSetChanged() } } inline fun RecyclerView.Adapter.notifyDataSetChangedAfter(action:() -> Unit){ action() notifyDataSetChanged() }
  60. 75 class SpeakersAdapter : RecyclerView.Adapter<SpeakersAdapter.ViewHolder>() { … fun addAllSpeakers(speakers: List<Speaker>)

    { speakerList.clear() speakerList.addAll(speakers) notifyDataSetChanged() } } inline fun RecyclerView.Adapter.notifyDataSetChangedAfter(action:() -> Unit){ action() notifyDataSetChanged() } inline fun RecyclerView.Adapter.notifyDataSetChangedAfter(action:() -> Unit){ action() notifyDataSetChanged() } class SpeakersAdapter : RecyclerView.Adapter<SpeakersAdapter.ViewHolder>() { … fun addAllSpeakers(speakers: List<Speaker>) { speakerList.clear() speakerList.addAll(speakers) notifyDataSetChanged() } }
  61. 76 inline fun RecyclerView.Adapter.notifyDataSetChangedAfter(action:() -> Unit){ action() notifyDataSetChanged() } inline

    fun RecyclerView.Adapter.notifyDataSetChangedAfter(action:() -> Unit){ action() notifyDataSetChanged() } class SpeakersAdapter : RecyclerView.Adapter<SpeakersAdapter.ViewHolder>() { … fun addAllSpeakers(speakers: List<Speaker>) = notifyDataSetChangedAfter { speakerList.clear() speakerList.addAll(speakers) } }
  62. 77 inline fun RecyclerView.Adapter.notifyDataSetChangedAfter(action:() -> Unit){ action() notifyDataSetChanged() } fun

    addAllSpeakers(speakers: List<Speaker>) = notifyDataSetChangedAfter { speakerList.clear() speakerList.addAll(speakers) } inline fun RecyclerView.Adapter.notifyDataSetChangedAfter(action:() -> Unit){ action() notifyDataSetChanged() }
  63. 80 inline fun View.onClick(crossinline block: (View) -> Unit) { setOnClickListener

    { block(this} } } searchIcon.setOnClickListener { //do something }
  64. 81 inline fun View.onClick(crossinline block: (View) -> Unit) { setOnClickListener

    { block(this} } } searchIcon.onClick { //do something }
  65. 85 •make sure you are adding value to your code

    •prefer readability over conciseness Guidelines
  66. 86 •make sure you are adding value to your code

    •prefer readability over conciseness Guidelines
  67. 88 •make sure you are adding value to your code

    •prefer readability over conciseness •be careful with optimizations Guidelines
  68. Leverage features unique to Kotlin 92 •use extension functions and

    properties to alias robust API •use inline modifier
  69. Leverage features unique to Kotlin 94 •use extension functions and

    properties to alias robust API •use inline modifier
  70. Leverage features unique to Kotlin 95 •use extension functions and

    properties to alias robust API •use inline modifier •use reified types
  71. Leverage features unique to Kotlin 96 •use extension functions and

    properties to alias robust API •use inline modifier •use reified types •use object destructuring
  72. 97