LiveData & MVVM

LiveData & MVVM

LiveData class

LiveData

LiveData is an abstract class called observable data holder in charge of keeping information about the data and notifying all interested observers if changes occur.

LiveData is actually just an Abstract Class. So it can’t be used as itself.

mediator_live_data_diagram

The most important feature of LiveData is that it is aware of the life cycle of other components of the application (activities, fragments…). Precisely because of this feature, LiveData forwards data only to observers who are in active state (if the life cycle is in STARTED or RESUMED state), while inactive (destroyed) observers, although registered, are not notified of the values ​​stored by LiveData. When we use LiveData we don’t need to worry when the life cycle of the activity/fragment ends (destroy) because they are automatically logged out as soon as the component completes its life cycle.

MutableLiveData

With the LiveData class, the setter methods are private and cannot be used if it is necessary to change the data somewhere, ie. set them. For this reason, there is its subclass MutableLiveData where the setter method public is setValue() (the postValue() method is used for the background thread).

MediatorLiveData class

LiveData subclass which may observe other LiveData objects and react on OnChanged events from them.

This is an even more specific method and has the added ability to track results from different LiveData and merge them into a single MediatorLiveData.
If I assume that we have two LiveData objects that emit some value but from two different sources (eg the value of a good from two different stock exchanges) then we want to listen to changes from both sources.

mediator_live_data_diagram

  • addSource(LiveData source, Observer onChanged)
    The method allows to choose which LiveData object is listened to, and what the callback method onChanged will do when a change occurs.
    MediatorLiveData has two parameters, the first is the LiveData that you want MediatorLiveData to observe, and the second is a callback that will fire when the data in the LiveData changes (passed in the first parameter)

  • removeSource(LiveData toRemove)
    Method allows to stop listening for LiveData changes. As in the following example, when after 10 data changes, the changes are no longer tracked:

Observe in View

Some of the code inside the View is the same as LiveData, so a MediatorLiveData view might look like this:

View – ViewModel

This example shows the procedure necessary for communication between View (activity) and its ViewModel:

  1. Implementation of the library in the project



    Also, in gradle, put google() in the repository section:

  2. MutableLiveData in ViewModel class

    • Creating the ViewModel class

      extending ViewModel (when we don’t need Context) or AndroidViewModel classes (when we need Context):

      We use this class if we don’t need Context:

      AndroidViewModel is used if context is required. To obtain context within the class, use the method getApplication () or pass Application through the class constructor.

    • Creating a MutableLiveData object

    • Creating a getter for a MutableLiveData object

      Although the “mHasSearchResults” object itself has mutable values and is represented as MutableLiveData, we with the getter always return the same object, and for that reason the LiveData object is used here and not MutableLiveData:

    • Broadcast changes

      The next thing to do within the ViewModel class is to broadcast changes to all registered observers. This is done using the MutableLiveData methods: setValue() (from the main thread) or postValue() (from the background thread):

      Calling the methodsetValue(T) and the previous example results in calling the onChanged() methods of the observer (usually a View) with the values sent through the parameter of the setValue() method (in this case “true”).

      NOTE:
      setValue() cannot be called from a background thread, in that case it is necessary to use postValue()

  3. Track changes to MutableLiveData in the View class

    • Reference to ViewModel in View:

    • Creating a reference to a LiveData object

      Now that we have a reference to the ViewModel, we can also access its methods, which we will use to call the getter method with which we will also get a reference to the LiveData object:

    • Observe (listening for changes)

      Once we have a reference to the LiveData object we can call its observe() method. This method accepts two parameters:

      • “LifecycleOwner” – we define which View LiveData should pay attention to when it comes to LifeCycle
      • “Observer” – through this parameter creating a new anonymous observer (Observer) we practically define what the observer will do after the change:

Repository – ViewModel – View

mvvm diagram

In the MVVM architecture, each level has direct access to only one level below it, while it is not interested in those above it. This kind of architecture allows us to change the layer above (eg View) without changing the level below, ie. source of information (eg ViewModel).

The entire communication between these architectural layers is actually a closed circle. The beginning of communication is usually the interaction of the user with the interface (View), which forwards the information “down” all the way to the database (through setValue()). A change in the database is the trigger for emitting changes in the Repository class. These changes are listened to by the ViewModel, and then used to broadcast its information to the View, which then uses it to update the content. This case is practically an extended case of communication between View and ViewModel from the previous section.

Repository

Repositor is a class that allows data access to other parts of the program, and it only has access to different sources of information (web or, as in this case, database). To get information from the database, the repository accesses the class responsible for direct access to the database (in this example, it is the “AppDBHelper” class)

We cannot call this handler’s method from other parts of the code because the rest of the program has access only to the repository-jy, then it is necessary to create a method that abstracts the handler:

As we have seen so far in the work with LiveData, in order to broadcast the change of a variablethree things are required:

  1. Creation of MutableLiveData objects (everything starts here and a new object is created)
  2. Getter of that MutableLiveData object
  3. Setting the new value of the variable with the setValue() method (in this example, that new value is the list we got directly from the Db.
    We will define the second and third items together in one method:

ViewModel

In the ViewModel class, the first item is also the creation of MutableLiveData objects. However, there is a slight difference here compared to the previous example (communication only between View and ViewModel), because a new MutableLiveData object is not created, but that ViewModel object is obtained from the Repository via a getter:

The second item is to create a getter for that LiveData object (which will be used by the View):

And the third item is broadcasting the changes (setValue() or postValue()). Usually these changes happen when one of the Repository methods is called to insert, update or remove data from the database.

View

The View is responsible for starting the whole chain of events by accepting the interaction of the end user (who can make some change that will affect the contents of the database). Then that user request is forwarded further “down” (View -> VieModel -> Repository -> DB). Acceptance

Within this method, the method that will eventually affect the database change is called:

Or

When there are changes in the database View needs that new information. For this reason, the View monitors and listens for changes to the LiveDate object (the beginnings of which extend to the tail) and reacts to them by updating the content:

You can see the entire project code in these examples on GitHub under the project “SQLdatabaseWithoutLibrary”.

Fragment – Fragment

Communication takes place through a shared ViewModel, so that all Views (fragments and activity) can access it, and “listen” for changes emitted by the ViewModel.

fragment and viewmodel

When fragmentA wants to send a message to FragmentB, it is enough to update the LiveData in the ViewModel, and the ViewModel will “broadcast” those changes to the air, so the listening FragmentB will be notified of the message and will be able to react accordingly. If the Activity is listening for changes in the same LiveData object, it will be notified at the same time, so it will be able to react adequately.

The advantages of this communication are as follows:

  1. The activity does not have to know anything and do anything about the conversation between the two fragments (using the listener pattern code related to the conversation is located within the Activity).
  2. Fragments do not need to know about each other, if one of the fragments goes down, the other continues to work as usual.
  3. Each fragment has its ownlife cycle and is not affected by the life cycle of another. If one fragment replaces another, the UI continues to work without problems.
Example




See the full sample project on GitHub under the “FragmetCommunication” repository.

LiveData class

LiveData is an abstract class called observable data holder in charge of keeping information about the data and notifying all interested observers if changes occur.

LiveData is actually just an Abstract Class. So it can’t be used as itself.

mediator_live_data_diagram

The most important feature of LiveData is that it is aware of the life cycle of other components of the application (activities, fragments…). Precisely because of this feature, LiveData forwards data only to observers who are in active state (if the life cycle is in STARTED or RESUMED state), while inactive (destroyed) observers, although registered, are not notified of the values ​​stored by LiveData. When we use LiveData we don’t need to worry when the life cycle of the activity/fragment ends (destroy) because they are automatically logged out as soon as the component completes its life cycle.

MutableLiveData class

Since LiveData is an abstract class it cannot be used independently, for that reason there is its subclass MutableLiveData which has public methods setValue() and postValue() to define the values that observers follow:

  • setValue() is called when we update the value from MainThread
  • postValue() is called when we update values from another thread.

So that any View that tracks changes to some value set via the setValue()/postValue() method can update the UI when a LiveData object changes.

MediatorLiveData class

LiveData subclass which may observe other LiveData objects and react on OnChanged events from them.

This is an even more specific method and has the added ability to track results from different LiveData and merge them into a single MediatorLiveData.
If I assume that we have two LiveData objects that emit some value but from two different sources (eg the value of a good from two different stock exchanges) then we want to listen to changes from both sources.

mediator_live_data_diagram

  • addSource(LiveData source, Observer onChanged)
    The method allows to choose which LiveData object is listened to, and what the callback method onChanged will do when a change occurs.
    MediatorLiveData has two parameters, the first is the LiveData that you want MediatorLiveData to observe, and the second is a callback that will fire when the data in the LiveData changes (passed in the first parameter)

  • removeSource(LiveData toRemove)
    Method allows to stop listening for LiveData changes. As in the following example, when after 10 data changes, the changes are no longer tracked:

Observe in View

Some of the code inside the View is the same as LiveData, so a MediatorLiveData view might look like this:

View – ViewModel

This example shows the procedure in which the ViewModel broadcasts changes in the state of a variable, while the state of that variable listens to the View (in this case, an activity) and then reacts to that change.

  1. Implementation of the library in the project



    Also, in gradle, put google() in the repository section:

  2. MutableLiveData in ViewModel class

    • Creating the ViewModel class

      extending ViewModel (when we don’t need Context) or AndroidViewModel classes (when we need Context):

      We use this class if we don’t need Context:

      AndroidViewModel is used if context is required. To obtain context within the class, use the method getApplication () or pass Application through the class constructor.

    • Creating a MutableLiveData object

    • Creating a getter for a MutableLiveData object

      Although the “mHasSearchResults” object itself has mutable values and is represented as MutableLiveData, we with the getter always return the same object, and for that reason the LiveData object is used here and not MutableLiveData:

    • Changes in the value of a variable caused by the broadcast of news about it

      In order to broadcast news from the viewModel about a change in the value of a variable, it is necessary to make that change. This is done using the MutableLiveData method:

      • setValue() (from the main thread)
      • postValue() (from background thread)
      Example

      NOTE:
      setValue() cannot be called from a background thread, in that case it is necessary to use postValue()

      After the variable’s value has been changed, the ViewModel broadcasts a “news” to all registered observers that changes have occurred, which then results in the observer’s onChanged() method being executed with the new values passed (through the setValue() method parameter, in this example “true”).

      NOTE:
      The setValue() method of the LiveData object, which changes the value of the variable, can be called from any file where we have a reference to that LiveData object (in this example, that object is mHasSearchResults). It can be a View that accepts a user action and sets a new value based on it, or a file that registers a change in the database and then broadcasts those changes…

  3. Track changes to MutableLiveData in the View class

    • Reference to ViewModel in View:

    • Creating a reference to a LiveData object

      Now that we have a reference to the ViewModel, we can also access its methods, which we will use to call the getter method with which we will also get a reference to the LiveData object:

    • Observe (listening for changes)

      Once we have a reference to the LiveData object we can call its observe() method. This method accepts two parameters:

      • “LifecycleOwner” – we define which View LiveData should pay attention to when it comes to LifeCycle
      • “Observer” – through this parameter creating a new anonymous observer (Observer) we practically define what the observer will do after the change:

      NOTE:
      With the previous code within onChanged() we react only to the change of the value we monitor, however sometimes it is necessary to have the value saved by the live object even before the change itself (eg set the initial value before any change occurs…). In such a case, getting the current value stored by the LiveData object is done using its getValue() method (it returns the current value stored by that object):

Repository – ViewModel – View

In the MVVM architecture, each level has direct access to only one level below it, while it is not interested in those above it. This kind of architecture allows us to change the layer above (eg View) without changing the level below, ie. source of information (eg ViewModel).

The entire communication between these architectural layers is actually a closed circle. The beginning of communication is usually the interaction of the user with the interface (View), which forwards the information “down” all the way to the database (through setValue()). A change in the database is the trigger for emitting changes in the Repository class. These changes are listened to by the ViewModel, and then used to broadcast its information to the View, which then uses it to update the content. This case is practically an extended case of communication between View and ViewModel from the previous section.

Repository

Since Repositoru is the only one that has direct access to the source of information (in this case, it is the database), we create a method in it whose goal is to get that information through the classes in charge of access to the database (in this example, it is the “AppDBHelper” class)

As we have seen so far in working with LiveData in a class that emits information three things are needed:

  1. Creation of MutableLiveData objects (everything starts here and a new object is created)
  2. Getter of that MutableLiveData object
  3. Emitting information with the setValue() method (this information is obtained from the Db by the previous method “getAllItems()”
    We will define the second and third items together in one method:

ViewModel

In the ViewModel class, the first item is also the creation of MutableLiveData objects. However, there is a slight difference here compared to the previous example (communication only between View and ViewModel), because a new MutableLiveData object is not created, but that ViewModel object is obtained from the Repository via a getter:

The second item is to create a getter for that LiveData object (which will be used by the View):

And the third item is broadcasting the changes (setValue() or postValue()). Usually these changes happen when one of the Repository methods is called to insert, update or remove data from the database.

View

The View is responsible for starting the whole chain of events by accepting the interaction of the end user (who can make some change that will affect the contents of the database). Then that user request is forwarded further “down” (View -> VieModel -> Repository -> DB). Acceptance

Within this method, the method that will eventually affect the database change is called:

Or

When there are changes in the database View needs that new information. For this reason, the View monitors and listens for changes to the LiveDate object (the beginnings of which extend to the tail) and reacts to them by updating the content:

You can see the entire project code in these examples on GitHub under the project “SQLdatabaseWithoutLibrary”.

Fragment – Fragment

Communication takes place through a shared ViewModel, so that all Views (fragments and activity) can access it, and “listen” for changes emitted by the ViewModel.

fragment and viewmodel

When fragmentA wants to send a message to FragmentB, it is enough to update the LiveData in the ViewModel, and the ViewModel will “broadcast” those changes to the air, so the listening FragmentB will be notified of the message and will be able to react accordingly. If the Activity is listening for changes in the same LiveData object, it will be notified at the same time, so it will be able to react adequately.

The advantages of this communication are as follows:

  1. The activity does not have to know anything and do anything about the conversation between the two fragments (using the listener pattern code related to the conversation is located within the Activity).
  2. Fragments do not need to know about each other, if one of the fragments goes down, the other continues to work as usual.
  3. Each fragment has its own life cycle and is not affected by the life cycle of another. If one fragment replaces another, the UI continues to work without problems.
Example




See the full sample project on GitHub under the “FragmetCommunication” repository.