Using KTX libraries

Florina Muntenescu
Android Developers
Published in
4 min readDec 15, 2020

--

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!

--

--