6 Results for: MediatorLiveData

Step #4: Augmenting Our Motor

  • for that is MediatorLiveData. MediatorLiveData is what underlies Transformations.map(). 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 retrieved from ToDoRepository via a call to items() (or null when we start out) load() will remove lastSource from the MediatorLiveData, make a fresh call to items() to get the items based on the new FilterMode, add that LiveData to the MediatorLiveData (with a lambda to convert the list of models into a RosterViewState), and hold onto that LiveData in lastSource init {} triggers our initial load() call, to retrieve ALL items Overall, RosterMotor.kt should now be: And, if you run the app, it should work as it did before, showing you all of the to-do items in the list.

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

Reading, Writing, and Debugging Storage

  • version of MainMotor, uses a MediatorLiveData to get the StreamResult objects to MainActivity: …whereas the Kotlin version uses MutableLiveData and viewModelScope for getting the coroutine results over to LiveData and from there to MainActivity: Note that we do not have a separate view-state class — instead, we just pass StreamResult along to the activity. That works in this case because we had no conversions that we needed to perform on the data from the repository. The exact locations of internal

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

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.