10 Results for: MediatorLiveData

Step #4: Augmenting Our Motor

  • RosterListFragment if there always is one LiveData supplying the RosterViewState objects, rather than having to know to subscribe to different LiveData objects at different times for different reasons. So, we have one or more LiveData objects with our items, and we want to funnel them all into a single LiveData of RosterViewState objects. One solution for that is MediatorLiveData. MediatorLiveData can observe one or several other LiveData objects. When values change in those LiveData objects, MediatorLiveData will invoke a lambda expression that you provide. There, you can convert the value to whatever you need and update the MediatorLiveData itself based on that change. You can add and remove LiveData objects from the MediatorLiveData whenever you need. So, what RosterMotor is doing is using a MediatorLiveData as the stable LiveData that RosterListFragment observes. As we change filter modes, we swap in a new LiveData source for the MediatorLiveData. With all that in mind… _states is our MediatorLiveData, and it is private Since we do not want RosterListFragment to know the implementation details, states is a plain LiveData property that happens to point to the MediatorLiveData in _states lastSource is the last LiveData that we create from the Flow that we got from ToDoRepository via a call to items() load() will remove lastSource from the MediatorLiveData, make a fresh call to items() to get the items based on the new FilterMode, add the resulting LiveData to the MediatorLiveData (with a lambda to convert the list

Reading, Writing, and Debugging Storage

  • with TextRepository to load and save our text. As with most things, this is incrementally easier with Kotlin. Ideally, the activity would have a stable LiveData to observe for getting the content to display in the editor. However, our TextRepository returns a LiveData for each load request. Somehow, we need to “pour” all of those individual LiveData objects into a single LiveData that the activity observes. The solution for that is MediatorLiveData. MediatorLiveData can observe one or several other LiveData objects. When values change in those LiveData objects, MediatorLiveData will invoke a lambda expression that you provide. There, you can convert the value to whatever you need and update the MediatorLiveData itself based on that change. You can add and remove LiveData objects from the MediatorLiveData whenever you need. So, MainMotor uses a MediatorLiveData as the stable LiveData that the activity observes. As we change sources of text content, we swap in a new LiveData source for the MediatorLiveData: Note

Monitoring Work

  • is in a DownloadViewModel: The doTheDownload() method will be called when the user clicks a button in the UI of MainActivity. That triggers our creation of the work request. DownloadViewModel takes the MediatorLiveData approach seen elsewhere in the book. Consumers of the DownloadViewModel, such as our MainActivity, have access to a liveWorkStatus field that represents the outbound stream of work status updates. For each doTheDownload() call, we chain the LiveData for this individual download onto the MediatorLiveData, removing it as a source once the State reaches a terminal condition (isFinished(), which will be true for a State of SUCCEEDED, FAILED, or CANCELED). The result is that our MainActivity can observe liveWorkStatus, without having to worry about individual LiveData objects from individual download requests. MainActivity observes liveWorkStatus and uses it to display a Toast when the download is finished: MainActivity — and its activity_main layout resource — use data binding. Partially

Comparing to LiveData

  • Android developers will look at StateFlow and MutableStateFlow and wonder if Kotlin just did a search-and-replace on the string “LiveData”. There seems little doubt that some aspects of StateFlow were inspired by LiveData. However, there are some noteworthy differences. LiveData can be observed only on the main application thread. There is no form of observe() that takes a parameter (e.g., a Looper or Executor) that LiveData might use as a way to deliver results on another thread. This thread limitation is fine for “last mile” sorts of delivery to results to the UI layer, but it can be annoying in other cases. If, say, you have a Room database DAO that has a function that returns a LiveData, you need to try to avoid observing it until you get to the UI. Anything between the DAO and the UI needs to try to work with Transformations and MediatorLiveData to manipulate the LiveData stream while not consuming that stream and causing work to be done on the main application thread. Contrast that with StateFlow, where you can use operators like flowOn() to control the dispatcher that gets used by default for downstream collectors of the emitted objects. By using Dispatchers.Main, you can get equivalent results as what you get from LiveData, with the flexibility of using other dispatchers where they may be needed. Compounding the problems of the previous segment is the fact that LiveData has very few operators. We have map() and switchMap() on Transformations… and that is about it. Third-party libraries could

Room and LiveData

  • , lest you accidentally wind up with a memory leak. And LiveData is lightweight by design. If you have complex background operations to perform, such as combining results from multiple sources and converting those results into specific object types, LiveData has limited facilities to help with that. And, what it does have can be somewhat arcane to use (e.g., MediatorLiveData).

Consuming Coroutines in a ViewModel

  • a CoroutineContext as a parameter. This can include the dispatcher used for the CoroutineScope: Also note that the LiveDataScope in which our lambda expression is executed has not only emit() but emitSource(). emit() takes an object to be emitted; emitSource() takes another LiveData of objects to be emitted. This works akin to addSource() on a MediatorLiveData, where any objects emitted by the LiveData passed into emitSource() are themselves emitted by the LiveData that liveData() returns: The combination of emit

Forecasting the Weather

  • of the temperature. MainMotor will get the WeatherResult from the Web service and convert it into a MainViewState object as they come in. MainViewState and RowState look a lot like WeatherResult and Forecast, just with the single temperature field: As with some of the previous examples, MainMotor uses MediatorLiveData, to fold one or more load() calls into a single results field that contains our LiveData of MainViewState objects: To create the temperature string, we use a string resource that contains

Handling Changes to LiveData

  • Observable for simpler subscription management, but you get new Observable objects from some API (e.g., new Retrofit calls). So, you use a Subject, such as a BehaviorSubject as the stable Observable, and you have the on-the-fly Observables feed their output into the Subject as input. The equivalent approach with LiveData is MediatorLiveData. MediatorLiveData is designed to take output from another LiveData as input, emitting those same objects. Those LiveData inputs can come and go, and observers can just observe the MediatorLiveData. The Sensor/LiveMediator sample project uses this approach. The activity, layout, and SensorLiveData are the same as in the previous example. What differs is the SensorViewModel, and what sort of object is used for the sensorLiveData field. Previously, that was the SensorLiveData itself. Now, it is a MediatorLiveData: When we create the SensorViewModel, we still create an instance of SensorLiveData. But then we add it as a source to the MediatorLiveData via addSource(). addSource() takes two parameters: The LiveData to be added as a source, and A lambda, method reference, or other Observer to say what should happen when events are emitted by that source In this case, we use a method reference to feed the output from the SensorLiveData directly into the MediatorLiveData. In other circumstances, you might have a lambda that performs some sort of data conversion. The behavior of the app overall does not change, because the events emitted by the SensorLiveData flow through

Writing a Transformation

  • , and so it is unavailable for older devices. So, we have a Confirmer interface that fills that role. The test() method on a Confirmer needs to return true for objects that should pass the filter, false otherwise. The filter() method on LiveTransmogrifiers takes a LiveData of some type and a Confirmer of that type. It then uses a MediatorLiveData, which is a LiveData object that can chain onto an existing LiveData and expose the onChanged() method for outside parties to use. In this case, our lambda uses the Confirmer to see if the new object passes the test(), and if it does, we call setValue() on the MediatorLiveData to have that object flow along to anything that observes that MediatorLiveData. filter() then returns that MediatorLiveData. The net effect is as if filter() wraps the original LiveData in another LiveData that applies our filtering rule. We can now use filter() to limit the readings that we get from the sensor: We pass our original SensorLiveData to filter(), along with a Confirmer

Monitoring Work

  • is in a DownloadViewModel: The doTheDownload() method will be called when the user clicks a button in the UI of MainActivity. That triggers our creation of the work request. DownloadViewModel takes the MediatorLiveData approach described in the chapter on LiveData and data binding. Consumers of the DownloadViewModel, such as our MainActivity, have access to a liveWorkStatus field that represents the outbound stream of work status updates. For each doTheDownload() call, we chain the LiveData for this individual download onto the MediatorLiveData, removing it as a source once the State reaches a terminal condition (isFinished(), which will be true for a State of SUCCEEDED, FAILED, or CANCELED). The result is that our MainActivity can observe liveWorkStatus, without having to worry about individual LiveData objects from individual download requests. MainActivity observes liveWorkStatus and uses it to display a Toast when the download is finished: MainActivity — and its activity_main layout resource — use

Search Syntax

You can search on individual terms (RecyclerView) or phrases ("configuration change").

A search on multiple terms (RecyclerView "configuration change") is considered to be a logical OR — the search results will show pages that refer to either option.

You can use AND, OR, NOT, and parentheses to construct search queries (e.g., RecyclerView AND "configuration change").

You can use * as a wildcard in an individual term (List*).

Color Coding

Green-bordered search hits are from second-generation CommonsWare books on Android app development. They will mostly show Kotlin and will use the AndroidX/Jetpack libraries.

Yellow-bordered search hits are from first-generation CommonsWare books on Android app development. They will mostly show Java and will use the Android Support Library.