Detailed LiveData of Android Jetpack Components

PS: The original text was first published on WeChat public account: Jongxingzhi (jzman-blog)

LiveData is an observable data holder class. Unlike regular observables, LiveData is lifecycle aware. LiveData is also part of the Android Jetpack component. This article will learn LiveData from the following aspects:

  1. What is LiveData
  2. Advantages of LiveData
  3. Use of LiveData
  4. Custom Livedata
  5. LiveData conversion

What is LiveData

LiveData is an observable data holder class. Unlike regular Observables, LiveData can perceive the life cycle of Activity, Fragment, and Service, ensuring that LiveData only updates component observers in the active life cycle state.

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

Advantages of LiveData

  • Keep the UI and the data consistent : LiveData follows the observer design pattern. When the life cycle changes, LiveData will notify the corresponding application component (Observer), and update the UI when the data changes.
  • Avoid memory leaks : This Observer is bound to Lifecycle objects. After the lifecycle of the Lifecycle object is destroyed, these Observers will also be automatically cleaned up.
  • Avoid crashing when Activity is in an inactive state : If the Observer is in an inactive state, the Observer will not receive any LiveData events.
  • No manual handling of the life cycle : UI components only observe related data, and will not stop or resume the observation. LiveData will be automatically managed according to changes in the specific life cycle.
  • Always keep the latest data : If the life cycle is inactive, the latest data will be received when the inactive state turns to the active state, such as switching from the background to the foreground to automatically receive the latest data.
  • Correctly handle configuration changes : If Activity or Fragment is re-created due to changes in device configuration, such as screen rotation, etc., it will immediately receive the latest data again.
  • Shared services : With the help of LiveData's data observation capabilities, services can be connected or disconnected at any time according to the lifecycle status of Livecycle.

Use of 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 makes us not save the data in the Activity or Fragment, but the data stored in the LiveData object Saving in the ViewModel reduces the workload of Activity and Fragment, so that Activity and Fragment are only responsible for interface management and display, instead of saving data, and the data is not affected when the configuration is changed.

Summary of LiceData usage:

  1. Create a specific LiveData instance to store data in ViewModel, as follows:
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 follows:
@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 LiveData's setValue or postValue to update the data, and then get the updated data in the observer, that is, Activity or Fragment, as follows:
public void setData(String data) {
    
    
    mData.setValue(data);
}

So how to create observers without LifecycleOwner? You can use the observeForever method of the LiveData object to add a class without LifecycleOwner to the observer list, as follows:

public class NoLifecycleOwner {
    
    
    public void print(NViewModel viewModel, final TextView tvData){
    
    
        //使用observeForever对象创建没有LifecycleOwner的观察者
        viewModel.getData().observeForever(new Observer<String>() {
    
    
            @Override
            public void onChanged(String s) {
    
    
                tvData.setText("我是没有LifecycleOwner的观察者:"+s);
            }
        });
    }
}

However, using observeForever to get the observer object will always be active. At this time, we need to manually call removeObserver(Observer) to remove the observer.

Custom Livedata

You can use the LiveData object with life cycle awareness to provide services to the outside world, and you can conveniently control the opening and closing of the service, as follows:

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

    @Override
    protected void onActive() {
    
    
        //有活跃观察者调用该方法
        //开启服务...
    }

    @Override
    protected void onInactive() {
    
    
        //没有任何活跃观察者调用该方法
        //结束服务...
    }
}

LiveData conversion

Lifecycle provides the tool class Transformations to convert the data types of LiveData. You can modify the specific types of data in LiveData before the data is returned to the observer. For example, the int type numbers 1, 2, etc. are converted into Chinese uppercase one, two, etc., So how to use it, create MapViewModel as follows:


/**
 * LiveData转换
 * 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) {
    
    
            //返回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) {
    
    
            //返回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 changes in the data in Activity, as follows:

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) {
    
    
                //数字转换为中文大写
                binding.tvData.setText(s);
            }
        });
    }

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

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

The only difference between the map and switchMap methods in the Transformations tool class is that map converts LiveData data to a specific type, such as String in the above code, while switchMap is LiveData. The getNumberCapital method in the above code is just a method of case conversion. , The test results are as follows:

jzman-blog

Both the map method and the switchMap method are internally converted by MediatorLiveData. You can use MediatorLiveData to implement more data type conversions like map and switch.

Practice it

Guess you like

Origin blog.csdn.net/jzman/article/details/105377520