Using KTX libraries
When using Android Java APIs in Kotlin, you quickly realise that you’re missing out on some of the Kotlin features that make coding so much easier and pleasant. Instead of writing your own wrappers and extension functions for these APIs, take a look at the Jetpack KTX libraries. Currently, more than 20 libraries have a KTX version, creating sweet idiomatic versions of Java APIs, ranging from Android platform APIs to ViewModels, SQLite and even Play Core. In this post we’ll look at some of the APIs available and peek under the hood to see how they were made.
If you prefer watching a video to reading a blog post check it out here:
Discoverability
As a best practice, to ease the discoverability of ktx functionality, always import the -ktx
artifact when available. As the -ktx
artifact depends transitively on the non-ktx version, you don’t need to include the other artifact. For example, for viewmodel
you get 2 artifacts: viewmodel
and viewmodel-ktx
. The -ktx
artifact will contain the Kotlin extensions:
// Java language implementation
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"// Kotlin implementation
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
Always import the -ktx artifact
For extensions on the Android platform APIs, import the core-ktx
artifact.
implementation "androidx.core:core-ktx:$corektx_version"
The majority of ktx functionality is implemented as extension functions so you’ll be able to easily find them by using auto-complete in Android Studio.
Other functionality, like the destructuring and operator overloading available on classes like Color
can be discovered by checking out the list of KTX extensions.
Platform APIs — core-ktx
core-ktx
provides idiomatic Kotlin functionality for APIs coming from the Android platform.
For example, if you’re working with SharedPreferences
, when you want to update a value, instead of executing 3 different calls, you can just do one:
Under the hood, the ktx edit method calls the same functionality, providing a good commit default option: apply()
. apply()
, unlike commit()
, commits the changes on disk asynchronously:
In core-ktx
you’ll also find an easier way of handling frequently used platform listeners. For example, if you want to trigger an action when text was changed in an EditText
, you’d have to implement all the methods of the TextWatcher
, even if you’re only interested in onTextChanged()
. core-ktx
creates the corresponding TextWatcher
methods: doOnTextChanged
, doAfterTextChanged
and doBeforeTextChanged
, but in your Kotlin code, you just use the one you need:
This brings several benefits: your code becomes easier to read, as it’s more concise and you get better naming and nullability annotations.
You’ll find similar listener APIs for AnimatorListener
and TransitionListener
.
Under the hood, doOnTextChanged
is implemented as an extension function on TextView
— the class that also has the addTextChangedListener
method. doOnTextChanged
creates empty implementations for the other functions of the TextWatcher
.
Jetpack APIs
The majority of extensions available are for Jetpack APIs. Here I’ll just go over some of the ones I found myself using most often.
LiveData
A lot of the LiveData functionality is implemented as extension functions as well: methods like map
, switchMap
or distinctUntilChanged
(source).
For example, using map
from livedata-ktx
removes the need to call Transformations.map(livedata) { /* map function */ }
, and allows us to call directly liveData.map
in a more Kotlin idiomatic way.
When you observe a LiveData
object, you’ll have to implement an Observer
. But using the observe from lifecycle-ktx
, the code becomes simpler. Make sure you call import androidx.lifecycle.observe
if the method isn’t found.
LiveData
is ideal for exposing data to be consumed by the UI so, to convert from Flow
to LiveData
and from LiveData
to Flow
, the lifecycle-livedata-ktx
artifact provides two handy extension functions: Flow.asLiveData()
and LiveData.asFlow()
.
Activity / Fragment and ViewModel
To construct a ViewModel, you would extend the ViewModel
class and implement ViewModelProvider.Factory
if your ViewModel
has dependencies. To instantiate it, use the viewModels
delegate (read more about delegates here): by viewModels(factory)
:
viewModels
is available in the -ktx
artifact of activity
and fragment
.
When working with coroutines, you’ll find yourself needing to launch a coroutine in the ViewModel. The work done by the coroutine should be cancelled when the ViewModel is destroyed. Instead of implementing your own CoroutineScope
, use viewModelScope
. The cancellation will be done automatically for you, in ViewModel.onCleared()
. Find out the ins and outs of viewModelScope
from this blog post.
Room and WorkManager
Both Room and WorkManager offer coroutines support via their -ktx
artifacts. As we think it’s worth covering those more in depth, stay tuned for MAD Skills articles focused on those specific libraries!
Other KTX modules
AndroidX artifacts are not the only ones to provide KTX versions:
- Firebase has created common Kotlin extensions
- Google Maps offers Maps and Places ktx libraries
- Play Core has a core-ktx artifact, providing coroutines support for monitoring in-app updates
Concise, readable and Kotlin idiomatic — these are the features that will benefit your code, once you start using -ktx
extensions. Stay tuned for more ways to take advantage of Kotlin and Jetpack in your app!