Slide 1

Slide 1 text

+ Android-KTX Small things to make our life easier

Slide 2

Slide 2 text

+ Rostyslav Lesovyi

Slide 3

Slide 3 text

+ Android-KTX ➙ Core KTX ➙ Activity KTX ➙ Fragment KTX ➙ ViewModel KTX ➙ Reactive Streams KTX

Slide 4

Slide 4 text

+ What and why? sharedPreferences.edit() .putString("key", "value") .apply() sharedPreferences.edit { putString("key", "value") } handler.postDelayed({ // do something }, 100) handler.postDelayed(100) { // do something }

Slide 5

Slide 5 text

+ Android-KTX Libraries Core KTX androidx.core.graphics androidx.core.view androidx.core.text …8 more... Fragment KTX Palette KTX SQLite KTX Collection KTX ViewModel KTX Reactive Streams KTX Navigation KTX WorkManager KTX

Slide 6

Slide 6 text

+ Core KTX androidx.core:core-ktx

Slide 7

Slide 7 text

+ Core KTX - androidx.core.animation animator.addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { // finished } }) animator.doOnStart { // finished } animator.doOnCancel { // cancelled } animator.doOnEnd { // ended }

Slide 8

Slide 8 text

+ Core KTX - androidx.core.content val array = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView) text = array.getString(R.styleable.MyCustomView_someAttr) array.recycle() context.withStyledAttributes(attrs, R.styleable.MyCustomView) { text = getString(R.styleable.MyCustomView_someAttr) }

Slide 9

Slide 9 text

+ Core KTX - androidx.core.content var wifiManager: WifiManager? // From Android 1 :) wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager // From Android 23 (Marshmallow) wifiManager = context.getSystemService(WifiManager::class.java) // KTX wifiManager = context.getSystemService()

Slide 10

Slide 10 text

+ Core KTX - androidx.core.util val placeholder = createBitmap(1, 1) lruCache( maxSize = 10, sizeOf = { key, value -> key.length + value.byteCount }, create = { placeholder }, onEntryRemoved = { _, _, bitmap, _ -> bitmap.recycle() })

Slide 11

Slide 11 text

+ Core KTX - androidx.core.util val array = SparseArray() val value = array[42] array[42] = "abc" 42 in array array.containsKey(42) array.containsValue("abc") array.getOrElse(42) { "abc" } array.forEach { key, value -> ... }

Slide 12

Slide 12 text

+ Core KTX - androidx.core.graphics val color = Color.CYAN val a = (color shr 24) and 0xff val r = (color shr 16) and 0xff val g = (color shr 8) and 0xff val b = color and 0xff val color = Color.CYAN val a = color.alpha val r = color.red val g = color.green val b = color.blue

Slide 13

Slide 13 text

+ Core KTX - androidx.core.graphics canvas.save() canvas.translate(10f, 10f) canvas.drawRect(0f, 0f, 20f, 20f, paint) canvas.restore() canvas.withTranslation(x = 10f, y = 10f) { drawRect(0f, 0f, 20f, 20f, paint) }

Slide 14

Slide 14 text

+ Core KTX - androidx.core.graphics.drawable val bounds = drawable.bounds drawable.setBounds(10, bounds.top, bounds.right, bounds.bottom) drawable.updateBounds(left = 10) val bitmap = drawable.toBitmap()

Slide 15

Slide 15 text

+ Core KTX - androidx.core.os TraceCompat.beginSection("my_trace_tag") // some stuff to benchmark TraceCompat.endSection() trace("my_trace_tag") { // some stuff to benchmark }

Slide 16

Slide 16 text

+ Core KTX - androidx.core.os val bundle = Bundle() bundle.putInt("key1", 42) bundle.putString("key2", "my string") val bundle = bundleOf( "key1" to 42, "key2" to "my string") Performance warning

Slide 17

Slide 17 text

+ Core KTX - androidx.core.os (bundleOf) fun bundleOf(vararg pairs: Pair) = Bundle(pairs.size).apply { for ((key, value) in pairs) { when (value) { null -> putString(key, null) // Any nullable type will suffice. // Scalars is Boolean -> putBoolean(key, value) is Byte -> putByte(key, value) is Char -> putChar(key, value) is Double -> putDouble(key, value) is Float -> putFloat(key, value) ...

Slide 18

Slide 18 text

+ Core KTX - androidx.core.os handler.postDelayed({ // do something }, delay) handler.postDelayed(delay) { // do something } handler.postAtTime({ // do something }, time) handler.postAtTime(time) { // do something }

Slide 19

Slide 19 text

+ Core KTX - androidx.core.text val string = " abc " val len = string.trim().length val len = string.trimmedLength() Bad Good

Slide 20

Slide 20 text

+ Core KTX - androidx.core.text Color Italic Bold Red fox jumped over the lazy dog

Slide 21

Slide 21 text

+ Core KTX - androidx.core.text val sb = SpannableString("Red fox jumped over the lazy dog") sb.setSpan(ForegroundColorSpan(Color.RED), 0, 3, 0) sb.setSpan(StyleSpan(Typeface.ITALIC), 8, 14, 0) sb.setSpan(StyleSpan(Typeface.BOLD), 24, 28, 0) Red fox jumped over the lazy dog Bad

Slide 22

Slide 22 text

+ Core KTX - androidx.core.text fun SpannableStringBuilder.append(text: CharSequence, span: Any) = apply { val len1 = length append(text) val len2 = length setSpan(span, len1, len2, 0) } Red fox jumped over the lazy dog Better

Slide 23

Slide 23 text

+ Core KTX - androidx.core.text val sb = SpannableStringBuilder() .append("Red", ForegroundColorSpan(Color.RED)) .append(" fox ") .append("jumped", StyleSpan(Typeface.ITALIC)) .append(" over the ") .append("lazy", StyleSpan(Typeface.BOLD)) .append(" dog") Red fox jumped over the lazy dog Better

Slide 24

Slide 24 text

+ Core KTX - androidx.core.text buildSpannedString { color(Color.RED) { append("Red") } append(" fox ") italic { append("jumped") } append(" over the ") bold { append("lazy") } append("dog") } Red fox jumped over the lazy dog Good

Slide 25

Slide 25 text

+ Core KTX - androidx.core.widget textView.doAfterTextChanged { text -> // do something } textView.doBeforeTextChanged { text, start, count, after -> // do something } textView.doOnTextChanged { text, start, count, after -> // do something }

Slide 26

Slide 26 text

+ Core KTX - androidx.core.view view.isGone = true view.isVisible = false view.isInvisible = true val start = view.marginStart val bottom = view.marginBottom view.updatePadding(left = 42) view.updatePaddingRelative(start = 42) view.postDelayed(100) { /* do something */ } val bitmap = view.drawToBitmap()

Slide 27

Slide 27 text

+ Core KTX - androidx.core.view val params = view.layoutParams as LinearLayout.LayoutParams params.weight = .42f view.layoutParams = params view.updateLayoutParams { weight = .42f }

Slide 28

Slide 28 text

+ Core KTX - androidx.core.view // instant or delayed view.doOnLayout { } // delayed view.doOnNextLayout { } // delayed, avoids memory leaks view.doOnPreDraw { }

Slide 29

Slide 29 text

+ Android - ViewTreeObserver risks View viewTreeObserver viewTreeObserver.addOnPreDrawListener(callback1) Detached

Slide 30

Slide 30 text

+ Android - ViewTreeObserver risks View viewTreeObserver > callback1 Detached

Slide 31

Slide 31 text

+ Window viewTreeObserver Android - ViewTreeObserver risks Detached Detached Attached View viewTreeObserver > callback1 Move callbacks

Slide 32

Slide 32 text

+ Window viewTreeObserver > callback1 Android - ViewTreeObserver risks View viewTreeObserver View viewTreeObserver onAttachedToWindow onDetachedFromWindow Detached Detached Attached View viewTreeObserver

Slide 33

Slide 33 text

+ Android - ViewTreeObserver risks View viewTreeObserver Detached viewTreeObserver.removeOnPreDrawListener(callback1)

Slide 34

Slide 34 text

+ Activity KTX androidx.activity:activity-ktx

Slide 35

Slide 35 text

+ Activity KTX activity.onBackPressedDispatcher.addCallback(lifecycleOwner) { // do something on back press isEnabled = false } class MyActivity : AppCompatActivity() { private val viewModel1 by viewModels() }

Slide 36

Slide 36 text

+ Fragment KTX androidx.fragment:fragment-ktx

Slide 37

Slide 37 text

+ Fragment KTX fragmentManager.commit(allowStateLoss = true) { add(MyFragment(), "my_tag") } class MyFragment : Fragment() { private val viewModel1 by viewModels() private val viewModel2 by activityViewModels() }

Slide 38

Slide 38 text

+ ViewModel KTX androidx.lifecycle:lifecycle-viewmodel-ktx

Slide 39

Slide 39 text

+ ViewModel KTX class MyViewModel : ViewModel(), CoroutineScope { private val job = SupervisorJob() override val coroutineContext = job + Dispatchers.Main override fun onCleared() { super.onCleared() job.cancel() } fun doWork() = launch { // main thread coroutine } }

Slide 40

Slide 40 text

+ ViewModel KTX class MyViewModel : ViewModel() { fun doWork() = viewModelScope.launch { // main thread coroutine } }

Slide 41

Slide 41 text

+ Reactive Streams KTX androidx.lifecycle:lifecycle-reactivestreams-ktx

Slide 42

Slide 42 text

+ Reactive Streams KTX val source: Publisher = ... val liveData: LiveData = source.toLiveData() val publisher: Publisher = liveData.toPublisher(lifecycleOwner)

Slide 43

Slide 43 text

+ More info: https://developer.android.com/kotlin/ktx

Slide 44

Slide 44 text

+ Q&A