About LiveDataBus

LiveData is a framework proposed by Android Architecture Components. LiveData is an observable data holding class that senses and follows the life cycle of components such as activities, fragments, or Services. Because of the component lifecycle awareness nature of LiveData, it is possible to update UI data only when the component is in the active state of the lifecycle.

② LiveData requires an Observer object, usually a concrete implementation of the Observer class. LiveData notifies the observer of data changes when the observer’s life cycle is STARTED or RESUMED. LiveData will not be notified even if its data changes while the observer is in another state.

The advantages of LiveData

(1) UI is consistent with LiveData because LiveData is in observer mode, so you can be notified when the data changes and update the UI.

② To avoid memory leaks, the observer is bound to the life cycle of the component. When the bound component is destroyed, the observer automatically cleans up its own data immediately.

(3) It will no longer crash when the Activity is in the stop state. For example, when the Activity is in the background state, it will not receive any events from LiveData.

(4) There is no need to solve the problem caused by the life cycle. LiveData can sense the life cycle of the bound component, and only when the active state will be aware of the data changes.

⑤ Real-time data refresh, when the component is in active state or inactive state to active state can always receive the latest data.

⑥ Solve the Configuration Change problem, when the screen rotates or is recycled and restarted, you can immediately receive the latest data.

Why replace EventBus and RxBus with LiveDataBus

The implementation of LiveDataBus is extremely simple. Compared with the complex implementation of EventBus, LiveDataBus only needs one class to implement.

LiveDataBus can reduce the size of the APK package, because LiveDataBus only relies on the official Android Architecture Components of LiveData, no other dependencies, its implementation only a class. For comparison, the EventBus JAR package size is 57kb, and RxBus relies on RxJava and RxAndroid, where the RxJava2 package size is 2.2MB, the RxJava1 package size is 1.1MB, and the RxAndroid package size is 9kb. Using LiveDataBus can greatly reduce the size of APK packages.

LiveDataBus only relies on The official Android Architecture Components of LiveData. Compared with RxJava and RxAndroid, LiveDataBus only relies on the official Android Architecture Components of LiveData. Dependent party support is better.

(4) LiveDataBus has life cycle awareness, LiveDataBus has life cycle awareness, in the Android system to use the caller does not need to call de-registration, compared to EventBus and RxBus more convenient, and there is no memory leak risk.

The composition of LiveDataBus

Messages can be any Object and can be of different types, such as Boolean and String. You can also define messages of a custom type.

② The message channel LiveData plays the role of the message channel. Different message channels are distinguished by different names. The name is of String type, and a LiveData message channel can be obtained by the name.

③ Message bus Message bus is implemented through singletons, and different message channels are stored in a HashMap.

The Observer subscriber gets the message channel through getChannel and calls Observe to subscribe to the message channel.

The publisher gets the message channel through getChannel and then calls setValue or postValue to publish the message.

Subscription registration

    LiveDataBus.get().with("MainActivity", HuaWei.class).observe(this, 
    new Observer<HuaWei>() {
              @Override
              public void onChanged(@Nullable HuaWei huaWei) {
                  if (huaWei != null)
                      Toast.makeText(MainActivity.this, huaWei.getName(),
                      Toast.LENGTH_SHORT).show();
              }
          });
Copy the code
  • Send a message
HuaWei = new HuaWei(" HuaWei ","P30Pro"); LiveDataBus.get().with("MainActivity",HuaWei.class).postValue(huaWei);Copy the code

The principle diagram of the LiveDataBus

LiveDataBus problems have occurred

  • For the first version of the LiveDataBus implementation, we found that during the use of the LiveDataBus, subscribers would receive messages published prior to the subscription. This is unacceptable for a message bus. In either EventBus or RxBus, the subscriber will not receive the message sent before the subscription. For a message bus, LiveDataBus has to solve this problem.

Summary of cause of LiveDataBus problem

  • For this problem, summarize the core cause of occurrence. For LiveData, its initial version is -1. When we call its setValue or postValue, its vesion will be +1. The initial version of the ObserverWrapper wrapper for each observer is also -1, that is, each newly registered observer has a version of -1. When LiveData sets the ObserverWrapper, if the version of LiveData is greater than the version of ObserverWrapper, LiveData forces the current value to be pushed to the Observer

LiveDataBus is finally implemented

  • LiveDataBus implementation

        public final class LiveDataBus {
        
            private final Map<String, MutableLiveData<Object>> bus;
        
            private LiveDataBus() {
                bus = new HashMap<>();
            }
        
            private static class SingletonHolder {
                private static final LiveDataBus LIVE_DATA_BUS = new LiveDataBus();
            }
        
            public static LiveDataBus get() {
                return SingletonHolder.LIVE_DATA_BUS;
            }
            public synchronized <T> MutableLiveData<T> with(String key,Class<T> type){
                if (!bus.containsKey(key)){
                    bus.put(key,new BusMutableLiveData<>());
                }
                return (MutableLiveData<T>) bus.get(key);
            }
        }

Copy the code
  • LiveDataBus reflection makes observer.mlastVersion = mVersion
        public class BusMutableLiveData<T> extends MutableLiveData<T> {
            @Override
            public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T>observer) { super.observe(owner, observer); try { hook(observer); } catch (Exception e) { e.printStackTrace(); }} /** * reflection makes observer.mlastVersion = mVersion ** @param observer ob */ private void hook(observer)<T>Observer) throws Exception {observer.mLastVersion = mVersion; The OnChange method will not be called back, so if you register // you will not receive the message // get the liveData class first<LiveData>classLiveData = LiveData.class; / / retrieved mObserver attribute in the class objects through reflection Field fieldObservers = classLiveData. GetDeclaredField (" mObservers "); / / set properties can be accessed fieldObservers setAccessible (true); Object objectObservers = fieldobServers.get (this); // Get the map object's type Class<?> classObservers = objectObservers.getClass();
                // Get all the get methods in the map object
                Method methodGet = classObservers.getDeclaredMethod("get", Object.class);
                // Set the get method to be accessible
                methodGet.setAccessible(true);
                // Execute the get method, passing in the objectObservers object, and then an Observer as the value of the key
                Object objectWrapperEntry = methodGet.invoke(objectObservers, observer);
                // Define an empty object
                Object objectWrapper = null;
                // Check whether objectWrapperEntry is of map. Entry type
                if (objectWrapperEntry instanceof Map.Entry) {
                    objectWrapper = ((Map.Entry) objectWrapperEntry).getValue();
                }
                if (objectWrapper == null) {
                    throw new NullPointerException("Wrapper can not be null!");
                }
        
                // Get the parent class of the object if it is not empty
                Class<? >classObserverWrapper = objectWrapper.getClass().getSuperclass(); / / by his parent class object, obtain mLastVersion Field Field fieldLastVersion = classObserverWrapper. GetDeclaredField (" mLastVersion "); fieldLastVersion.setAccessible(true); Field fieldVersion = classLiveData.getDeclaredField("mVersion"); fieldVersion.setAccessible(true); Object objectVersion = fieldVersion.get(this); // Set the mVersion field to mLastVersion fieldLastVersion.set(objectWrapper, objectVersion); }}Copy the code
LiveDataBusDemo

Thank you

Thanks for reading, and if you want to learn more, follow me

GitHub