Loading...
Home
  • Tech Blogs
  • Videos
  • Conferences
    • Droidcon News
    • Upcoming Conferences
    • Become a Partner
    • Past Events
    • Keep Me Informed
    • Diversity Scholarships
  • Community
    • droidcon Team
    • How to Hold a Droidcon
  • Android Careers
Sign In

Global CSS

 

LiveData transformations

 

 
Peter Törnhult
Just do it
Published: June 30, 2020
Tweet
Share
 


 

 

LiveData is a great tool to use when working with Android Architecture Components. Before I knew how to use the Transformations class, I was abusing LiveData and creating a lot of code-smell. Having worked with LiveData and Architecture components for a few years now I think I’ve figured out some good practices and patterns that I’d like to share with you.

 

The basics…

Transforming LiveData is pretty easy to do and there is an awesome helper class named Transformations for just this purpose. This class provides three static methods: map, switchMap and distinctUntilChanged which will be explained below. All of the examples below will use the following data class, which represents a player we receive from our database or backend API. This Player model just has a name and score field to make it easier for the examples, but in reality, it would have many more fields.

 

 

map

Transforms the value of LiveDatainto another value. Here’s a simple example of how this can be used:

 

 

switchMap

Transforms the value of a LiveDatainto another LiveData. switchMaptransformations can be a bit tricky, so let’s start with a simple example: We want to implement a basic search feature for players. Every time the search text changes we want to update search results. The following code shows how that works.

 

 

distinctUntilChanged

Filters LiveDataso that values will not be emitted unless they have changed. Many times we might be notified about a change that doesn’t contain any relevant changes. If we’re listening for the names of all players, we don’t want to update the UI when the score changes. This is where the distinctUntilChanged method is useful.

 

 

This is a really neat feature and I tend to use it quite often in my code. For my use-cases, it’s mostly related to RecyclerView/ adapter updates.

 

livedata-ktx extensions for Transformations

All of the Transformations class functions above are also available as extension functions on LiveData using the dependency:

androidx.lifecycle:lifecycle-livedata-ktx:<version>

With this you can, for instance, rewrite the example above as:

 

 

Behind the scenes of the Transformations class

We’ve just covered 3 simple transformations that you can actually write your self. All of them are written using the MediatorLiveData class. The MediatorLiveData class is the class I probably use most of all when dealing with LiveData (though I use map / switchMap / distinctUntilChangedwhen it makes sense).

To give you an example of when you should be creating your own MediatorLiveData class, look at this code:

 

 

By only mapping dbGame changes, I’m taking the current value of players (this.players.value) when the game is updated. So, I’m not updating the game when players are updated. To solve this I should instead use MediatorLiveData to merge players and games if any of them are updated. This would look something like this:

 

 

With this solution, I’m getting game updates whenever either players or dbGame is updated.

 

MediatorLiveData

MediatorLiveData can transform, filter and merge other LiveDatainstances. I tend to follow the same pattern whenever I create a MediatorLiveData, which looks something like this:

 

 

In this example, I’m observing two LiveData sources (a and b). I’m calling the update function when the mediator is created which will only emit a value if both sources are currently non-null. This pattern is very versatile, but let’s go through each step, one by one:

 

OPTION 1

What are the sources you want to monitor for changes before emitting anything from this LiveData. This can be just a single source (or more) but has no fixed upper limit. (I.e. lets you do a conditional map of a single LiveData or merge multiple LiveDatas)

 

OPTION 2

Call the inner update function here if you want to set an initial value when the MediatorLiveData is created. For simplicity's sake, I usually call my update function, but just setting the value /postValue of the MediatorLiveData also works. In some cases, I don’t want to emit an initial value as I’m expecting this to emit null if a or b isn’t yet set. I then just skip calling update or setting an initial value here.

 

OPTION 3

Since update will be called whenever either a or b emits an update we must expect a and b to be null. Sometimes you actually want to update your MediatorLiveData even though one or more sources are currently null, but this is a nice way of making sure the local variables aVal and bVal are non-null before emitting new values from the MediatorLiveData. You can even apply more validation/filtering here to reduce the emission of the final MediatorLiveData you’re creating.

 

OPTION 4

Since the MediatorLiveData is a LiveData instance we can either set the value (like in the example above) or call postValue (if for some reason you’re not on the main thread when emitting values). This is also where you decide how you want to transform the source data values. The example above just sums aVal and bVal, but you can of course apply any transformation you want here.

 

Conclusions

Use map, switchMap and distinctUntilChanged for all LiveData transformations. Avoid writing your own transformations unless necessary and try to combine operations to create more complex ones.

Use distinctUntilChanged to avoid emitting identical data, which causes unnecessary UI updates.

If you find yourself getting the current value of another LiveData using the .value property inside a map / switchMap or inside an observe-block, you should consider creating a MediatorLiveData to merge the sources properly instead.

 

Bonus

Now that you know how MediatorLiveData works you can go ahead and write your own amazing transformations. To get you started, here are some common extensions I’ve found or created for my own projects:

https://github.com/ptornhult/livedata-utils

Thanks to Erik Hellman. 

 

 

Tags: Livedata, Architecture Components, Mediatorlivedata, Transformations, Android

 

View original article at: 


 

Originally published: June 03, 2020

Android News
Getting… your BottomSheetScaffold working on Jetpack Compose Beta 03
Getting… your BottomSheetScaffold working on Jetpack Compose Beta 03

By Carlos Mota

It’s Monday, no releases this week, and… there’s a new version of Jetpack Compose — beta 03—available. What a perfect time to just increment 02 to 03 and see what’s new. The API is (almost) final so after updating from alpha to beta there weren’t any big changes to do. However, and remember that’s still in development, there’s always something that I need to update. 

By ProAndroidDev -
Android News
Noisy Code 🗣 With Kotlin Scopes
Noisy Code 🗣 With Kotlin Scopes

By Chetan Gupta

Scopes make your code more readable? think again... You are going to encounter these scope functions namely let, run, apply, also, within every Kotlin codebase, along with all the mischievous ways developers exploit their usage from the way they were intended for. Let see how popular opinion on those ends up just as a code noise.

By ProAndroidDev -
Android News
Improving Android DataBinding with Bindables library
Improving Android DataBinding with Bindables library

By Jaewoong Eum

DataBinding is one of the most important factors for MVVM architecture. The basic concept of DataBinding is to link the view and view model via observer patterns, properties, event callbacks, etc. Linking and automating communication between the view via the bound properties or something in the view model has a lot of benefits in the MVVM architecture concept.

By ProAndroidDev -
Android News
KMM QuickStart Guide
KMM QuickStart Guide

By Mayank Kharbanda

Kotlin Multiplatform (KMP) is a code-sharing technology that allows us to use the same code for different platforms whether it’s JVM, Android, iOS, watchOS, tvOS, Web, Desktop, or WebAssembly. In this article, we will use Kotlin Multiplatform Mobile (KMM) which is a subset of KMP with the focus on providing better tooling and support for sharing code on mobile platforms i.e. Android and iOS.

By ProAndroidDev -
droidcon News

Tech Showcases,

Developer Resources &

Partners

/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/home-details/EmployerBrandingHeader
EmployerBrandingHeader
https://jobs.droidcon.com/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/jobs-droidcon/jobs.droidcon.com
jobs.droidcon.com

Latest Android Jobs

http://www.kotlinweekly.net/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/kotlin-weekly/Kotlin Weekly
Kotlin Weekly

Your weekly dose of Kotlin

https://proandroiddev.com/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/pad/ProAndroidDev
ProAndroidDev

Android Tech Blogs, Case Studies and Step-by-Step Coding

/detail?content-id=/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Zalando/Zalando
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Zalando/Zalando
Zalando

Meet one of Berlin's top employers

/detail?content-id=/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Academy for App Success/Academy for App Success
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Academy for App Success/Academy for App Success
Academy for App Success

Google Play resources tailored for the global droidcon community

Follow us

Team droidcon

Get in touch with us

Write us an Email

 

 

Quicklinks

> Code of Conduct

> Terms and Conditions

> How to hold a conference

> FAQs

> Imprint

Droidcon is a registered trademark of Mobile Seasons GmbH Copyright © 2020. All rights reserved.

powered by Breakpoint One