banner
jzman

jzman

Coding、思考、自觉。
github

Android Jetpack Components: LiveData

PS: Time management is really important. The better you manage it, the higher your self-discipline will be. Let's motivate each other.

LiveData is an observable data holder class. Unlike regular observables, LiveData is lifecycle-aware. LiveData is also part of the Android Jetpack components. In this article, we will learn about LiveData from the following aspects:

  1. What is LiveData?
  2. Advantages of LiveData
  3. Using LiveData
  4. Customizing LiveData
  5. LiveData transformations

What is LiveData?#

LiveData is an observable data holder class. Unlike regular observables, LiveData is aware of the lifecycle of Activity, Fragment, and Service. It ensures that LiveData only updates the component observers that are in an active lifecycle state.

If the state of the application component observer is STARTED or RESUMED, LiveData considers the component to be active. The active component will receive data updates from LiveData, while other registered component observers will not receive any data updates.

Advantages of LiveData#

  • Maintains consistency between UI and data: LiveData follows the observer design pattern. When the lifecycle changes, LiveData notifies the corresponding application component (Observer), and when the data changes, it also notifies to update the UI.
  • Avoids memory leaks: The Observer is bound to the Lifecycle object, so when the Lifecycle object is destroyed, these Observers are automatically cleared.
  • Avoids crashes when the Activity is inactive: If the observer is in an inactive state, it will not receive any LiveData events.
  • No need to manually handle lifecycles: UI components only observe the relevant data and do not stop or resume observation. LiveData automatically manages it based on the specific lifecycle changes.
  • Always keeps the latest data: If the lifecycle is in a non-active state, it will receive the latest data when transitioning from a non-active state to an active state, such as automatically receiving the latest data when switching from the background to the foreground.
  • Handles configuration changes correctly: If the Activity or Fragment is recreated due to device configuration changes, such as screen rotation, it will immediately receive the latest data.
  • Shared services: With the observation capability of LiveData data, services can be connected or disconnected at any time based on the lifecycle state.

Using LiveData#

Define LiveData data in a specific ViewModel class, and then observe the changes of LiveData data in the corresponding Activity or Fragment. The use of LiveData allows us to store data in the ViewModel instead of the Activity or Fragment, reducing the workload of the Activity and Fragment. They only need to manage and display the interface, without saving data, and the data is not affected by configuration changes.

Summary of LiveData usage:

  1. Create a specific LiveData instance in the ViewModel to store data, as shown below:
public class MViewModel extends ViewModel {
    private MutableLiveData<String> data;
    public LiveData<String> getData(){
        if (data == null){
            data = new MutableLiveData<>();
            data.postValue(DataUtil.getData());
        }
        return data;
    }
}
  1. Use the observe or observeForever method of the LiveData object to add the corresponding Activity or Fragment as an observer of the LiveData object, as shown below:
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
    MViewModel mViewModel = ViewModelProviders.of(this).get(MViewModel.class);
    mViewModel.getData().observe(this, new Observer<String>() {
        @Override
        public void onChanged(String msg) {
            binding.setData(msg);
        }
    });
}
  1. Use the setValue or postValue method of LiveData to update the data, and then retrieve the updated data in the observer, which is the Activity or Fragment, as shown below:
public void setData(String data) {
    mData.setValue(data);
}

How to create an observer without a LifecycleOwner? You can use the observeForever method of the LiveData object to add a class without a LifecycleOwner as an observer to the observer list, as shown below:

public class NoLifecycleOwner {
    public void print(NViewModel viewModel, final TextView tvData){
        // Use observeForever to create an observer without a LifecycleOwner
        viewModel.getData().observeForever(new Observer<String>() {
            @Override
            public void onChanged(String s) {
                tvData.setText("I am an observer without a LifecycleOwner: " + s);
            }
        });
    }
}

However, using observeForever to obtain an observer object will keep it in an active state. In this case, you need to manually call removeObserver(Observer) to remove the observer.

Customizing LiveData#

LiveData objects can provide services with lifecycle awareness, making it easy to control the start and stop of services. For example:

/**
 * Custom LiveData
 * Powered by jzman.
 * Created on 2018/12/17 0017.
 */
public class CustomLiveData extends LiveData<String> {

    @Override
    protected void onActive() {
        // Called when there are active observers
        // Start the service...
    }

    @Override
    protected void onInactive() {
        // Called when there are no active observers
        // Stop the service...
    }
}

LiveData Transformations#

Lifecycle provides the Transformations utility class to transform the data type of LiveData. It allows modifying the data in LiveData before it is returned to the observer. For example, converting an integer number 1, 2, etc. to Chinese uppercase characters "壹", "贰", etc. How to use it? Create a MapViewModel as follows:

/**
 * LiveData transformations
 * Powered by jzman.
 * Created on 2018/12/17 0017.
 */
public class MapViewModel extends ViewModel {
    private MutableLiveData<Integer> mPrice = new MutableLiveData<>();

    // Map
    private LiveData<String> mMapPrice = Transformations.map(mPrice, new Function<Integer, String>() {
        @Override
        public String apply(Integer input) {
            // Return String
            return Util.getNumberCapital(input);
        }
    });

    // SwitchMap
    private LiveData<String> mSwitchMapPrice = Transformations.switchMap(mPrice, new Function<Integer, LiveData<String>>() {
        @Override
        public LiveData<String> apply(Integer input) {
            // Return LiveData
            MutableLiveData<String> data = new MutableLiveData<>();
            data.postValue(Util.getNumberCapital(input));
            return data;
        }
    });

    public void setPrice(int price) {
        mPrice.setValue(price);
    }

    public LiveData<String> getPrice() {
        // Map
        return mMapPrice;
        // SwitchMap
//        return mSwitchMapPrice;
    }
}

Then, observe the data changes in the Activity:

public class MapActivity extends AppCompatActivity {
    private MapViewModel mapViewModel;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        final ActivityMapBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_map);
        mapViewModel = new MapViewModel();
        mapViewModel.getPrice().observe(this, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                // Convert numbers to Chinese uppercase characters
                binding.tvData.setText(s);
            }
        });
    }

    public void btnSetPrice1(View view) {
        mapViewModel.setPrice(1);
    }

    public void btnSetPrice2(View view) {
        mapViewModel.setPrice(2);
    }
}

The Transformations utility class internally uses MediatorLiveData to perform the transformations. You can use MediatorLiveData to implement more data type transformations similar to map and switch.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.