Difference between revisions of "An Android Jetpack LiveData Tutorial"

From Techotopia
Jump to: navigation, search
(Implementing the Observer)
(Implementing the Observer)
 
Line 100: Line 100:
 
</pre>
 
</pre>
  
== Implementing the Observer ==
+
== Implementing the Observer ==
  
 
Now that the conversion result is contained within a LiveData instance, the next step is to configure an observer within the UI controller which, in  this example, is the MainFragment class. Locate the MainFragment.java class (app -> java -> <package name> -> MainFragment), double-click on it to load it into the editor and modify the onActivityCreated() method to create a new Observer instance named resultObserver:
 
Now that the conversion result is contained within a LiveData instance, the next step is to configure an observer within the UI controller which, in  this example, is the MainFragment class. Locate the MainFragment.java class (app -> java -> <package name> -> MainFragment), double-click on it to load it into the editor and modify the onActivityCreated() method to create a new Observer instance named resultObserver:
Line 160: Line 160:
 
                mViewModel.setAmount(Float.valueOf(
 
                mViewModel.setAmount(Float.valueOf(
 
dollarText.getText().toString()));
 
dollarText.getText().toString()));
                resultText.setText(mViewModel.getResult().toString());
 
 
            } else {
 
            } else {
 
                resultText.setText("No Value");
 
                resultText.setText("No Value");

Latest revision as of 18:37, 16 January 2019

PreviousTable of ContentsNext
An Android Jetpack ViewModel TutorialAn Overview of Android Jetpack Data Binding


You are reading a sample chapter from the Android Studio 3.2 Edition of this book.

Purchase the fully updated Android Studio Hedgehog Edition of this publication in eBook ($32.99) or Print ($49.99) format

Android Studio Hedgehog Essentials - Java Edition Print and eBook (PDF) editions contain 87 chapters and over 800 pages
Buy Print Preview Book


The previous chapter began the process of designing an app to conform to the recommended Jetpack architecture guidelines. These initial steps involved the selection of the Fragment+ViewModel project template and the implementation of the data model for the app user interface within a ViewModel instance.

This chapter will further enhance the app design by making use of the LiveData architecture component. Once LiveData support has been added to the project in this chapter, the next chapters (starting with An Overview of Android Jetpack Data Binding) will make use of the Jetpack Data Binding library to eliminate even more code from the project.


Contents


LiveData - A Recap

LiveData was introduced previously in the chapter entitled Modern Android App Architecture with Jetpack. As described earlier, the LiveData component can be used as a wrapper around data values within a view model. Once contained in a LiveData instance, those variables become observable to other objects within the app, typically UI controllers such as Activities and Fragments. This allows the UI controller to receive a notification whenever the underlying LiveData value changes. An observer is set up by creating an instance of the Observer class and defining an onChange() method to be called when the LiveData value changes. Once the Observer instance has been created, it is attached to the LiveData object via a call to the LiveData object’s observe() method.

LiveData instances can be declared as being mutable using the MutableLiveData class, allowing both the ViewModel and UI controller to make changes to the underlying data value.

Adding LiveData to the ViewModel

Launch Android Studio, open the ViewModelDemo project created in the previous chapter and edit the MainViewModel.java file which should currently read as follows:

package com.ebookfrenzy.viewmodeldemo.ui.main;
 
import android.arch.lifecycle.ViewModel;
 
public class  MainViewModel extends ViewModel {
 
    private static final Float usd_to_eu_rate = 0.74F;
    private String dollarText = "";
    private Float result = 0F;
 
    public void setAmount(String value) {
        this.dollarText = value;
        result = Float.valueOf(dollarText)*usd_to_eu_rate;
    }
 
    public Float getResult()
    {
        return result;
    }
}

The objective of this stage in the chapter is to wrap the result variable in a MutableLiveData instance (the object will need to be mutable so that the value can be changed each time the user requests a currency conversion). Begin by modifying the class so that it now reads as follows noting that an additional package needs to be imported when making use of LiveData:

package com.ebookfrenzy.viewmodeldemo.ui.main;
 
import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.ViewModel;
 
public class  MainViewModel extends ViewModel {
 
    private static final Float usd_to_eu_rate = 0.74F;
    private String dollarText = "";
    private MutableLiveData<Float> result = new MutableLiveData<>();
 
    public void setAmount(String value) {
        this.dollarText = value;
        result = Float.valueOf(dollarText)*usd_to_eu_rate;
    }
 
    public Float getResult()
    {
        return result;
    }
}

You are reading a sample chapter from the Android Studio 3.2 Edition of this book.

Purchase the fully updated Android Studio Hedgehog Edition of this publication in eBook ($32.99) or Print ($49.99) format

Android Studio Hedgehog Essentials - Java Edition Print and eBook (PDF) editions contain 87 chapters and over 800 pages
Buy Print Preview Book

Now that the result variable is contained in a mutable LiveData instance, both the setAmount() and getResult() methods need to modified. In the case of the setAmount() method, a value can no longer be assigned to the result variable using the assignment (=) operator. Instead, the LiveData setValue() method must be called, passing through the new value as an argument. As currently implemented, the getResult() method is declared as returning a Float value and now needs to be changed to return a MutableLiveData object. Making these remaining changes results in the following class file:

package com.ebookfrenzy.viewmodeldemo.ui.main;
 
import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.ViewModel;
 
public class  MainViewModel extends ViewModel {
 
    private static final Float usd_to_eu_rate = 0.74F;
    private String dollarText = "";
    private MutableLiveData<Float> result = new MutableLiveData<>();
 
 
    public void setAmount(String value) {
        this.dollarText = value;
        result.setValue(Float.valueOf(dollarText)*usd_to_eu_rate);
    }
 
    public MutableLiveData<Float> getResult()
    {
        return result;
    }
}

Implementing the Observer

Now that the conversion result is contained within a LiveData instance, the next step is to configure an observer within the UI controller which, in this example, is the MainFragment class. Locate the MainFragment.java class (app -> java -> <package name> -> MainFragment), double-click on it to load it into the editor and modify the onActivityCreated() method to create a new Observer instance named resultObserver:

package com.ebookfrenzy.viewmodeldemo.ui.main;
 
import android.arch.lifecycle.Observer;
.
.
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
.
.
    dollarText = getView().findViewById(R.id.dollarText);
    resultText = getView().findViewById(R.id.resultText);
    convertButton = getView().findViewById(R.id.convertButton);
 
    resultText.setText(mViewModel.getResult().toString());
 
    final Observer<Float> resultObserver = new Observer<Float>() {
        @Override
        public void onChanged(@Nullable final Float result) {
            resultText.setText(result.toString());
        }
    };
.
.
}

The resultObserver instance declares the onChanged() method which, when called, is passed the current result value which it then converts to a string and displays on the result TextView object. The next step is to add the observer to the result LiveData object, a reference to which can be obtained via a call to the getResult() method of the ViewModel object. Since updating the result TextView is now the responsibility of the onChanged() callback method, the existing lines of code to perform this task can now be deleted:

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    mViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
 
    dollarText = getView().findViewById(R.id.dollarText);
    resultText = getView().findViewById(R.id.resultText);
    convertButton = getView().findViewById(R.id.convertButton);
 
    final Observer<Float> resultObserver = new Observer<Float>() {
        @Override
        public void onChanged(@Nullable final Float result) {
            resultText.setText(result.toString());
        }
    };
 
    mViewModel.getResult().observe(this, resultObserver);
 
    convertButton.setOnClickListener(new View.OnClickListener()
    {
        @Override
        public void onClick(View v) {
 
            if (!dollarText.getText().toString().equals("")) {
                mViewModel.setAmount(Float.valueOf(
				dollarText.getText().toString()));
            } else {
                resultText.setText("No Value");
            }
        }
    });
}

Compile and run the app, enter a value into the dollar field, click on the Convert button and verify that the converted euro amount appears on the TextView. This confirms that the observer received notification that the result value had changed and called the onChanged() method to display the latest data.

Note in the above implementation of the onActivityCreated() method that the line of code responsible for displaying the current result value each time the method was called was removed. This was originally put in place to ensure that the displayed value was not lost in the event that the Fragment was recreated for any reason. Because LiveData monitors the lifecycle status of its observers, this step is no longer necessary. When LiveData detects that the UI controller was recreated, it automatically triggers any associated observers and provides the latest data. Verify this by rotating the device while a euro value is displayed on the TextView object and confirming that the value is not lost.

Summary

This chapter demonstrated the use of the Android LiveData component to make sure that the data displayed to the user always matches that stored in the ViewModel. This relatively simple process consisted of wrapping a ViewModel data value within a LiveData object and setting up an observer within the UI controller subscribed to the LiveData value. Each time the LiveData value changes, the observer is notified and the onChanged() method called and passed the updated value.

Adding LiveData support to the project has gone some way towards simplifying the design of the project. Additional and significant improvements are also possible by making use of the Data Binding Library, details of which are covered in the next chapter.


You are reading a sample chapter from the Android Studio 3.2 Edition of this book.

Purchase the fully updated Android Studio Hedgehog Edition of this publication in eBook ($32.99) or Print ($49.99) format

Android Studio Hedgehog Essentials - Java Edition Print and eBook (PDF) editions contain 87 chapters and over 800 pages
Buy Print Preview Book



PreviousTable of ContentsNext
An Android Jetpack ViewModel TutorialAn Overview of Android Jetpack Data Binding