Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to replace RxBus with LiveDataBus

2025-01-15 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article will explain in detail how to use LiveDataBus to replace RxBus. The content of the article is of high quality, so the editor shares it for you as a reference. I hope you will have a certain understanding of the relevant knowledge after reading this article.

For Android system, message passing is the most basic component. Different pages and different components in each App are passing messages. Message passing can be used not only for the communication between the four components of Android, but also between the asynchronous thread and the main thread. For Android developers, there are many kinds of message delivery methods, from the earliest use of Handler, BroadcastReceiver, interface callback, to the popular communication bus framework EventBus and RxBus in recent years. The Android messaging framework is always evolving.

Start with EventBus.

EventBus is an Android event publish / subscribe framework that simplifies Android event delivery by decoupling publishers and subscribers. EventBus can replace Android's traditional Intent, Handler, Broadcast or interface callback to pass data between Fragment, Activity, and Service threads and execute methods.

The most important feature of EventBus is simplicity and decoupling. Before there is no EventBus, we usually use broadcasts to listen, or custom interface function callbacks. In some scenarios, we can directly use Intent to carry simple data, or handle message delivery through Handler between threads. But whether broadcast or Handler mechanism is far from satisfying our efficient development. EventBus simplifies communication between components within an application and between components and background threads. As soon as EventBus was launched, it was respected by the majority of developers.

Now it seems that EventBus has brought a new framework and idea to the world of Android developers, that is, publishing and subscribing to messages. This idea has been applied in many subsequent frameworks.

The picture is taken from the EventBus GitHub home page.

Publish / subscribe model

The subscription publishing model defines an one-to-many dependency that allows multiple subscriber objects to listen on a topic object at the same time. When the subject object changes its own state, it notifies all subscriber objects so that they can update their status automatically.

The emergence of RxBus

RxBus is not a library, but a file, and the implementation is only 30 lines of code. RxBus itself does not require much analysis, and its strength comes entirely from the RxJava technology on which it is based. Responsive programming (Reactive Programming) technology has been very popular in recent years, and RxJava is its implementation on Java. RxJava is born in publish / subscribe mode and is easy to handle thread switching. Therefore, RxBus with only 30 lines of code, dare to challenge the status of EventBus "jianghu boss".

RxBus principle

There is a Subject class in RxJava, which inherits the Observable class and implements the Observer interface, so Subject can play the role of both subscribers and subscribers. We use PublishSubject, a subclass of Subject, to create a Subject object (PublishSubject sends received events to subscribers immediately after being subscribed), subscribes to the Subject object where it needs to receive events, and then sends them to the subscriber if the Subject object receives the event At this point, the Subject object acts as the subscriber.

After the subscription is completed, the event is sent to the previously subscribed Subject object where the event needs to be sent, then the Subject object receives the event as a subscriber, and then immediately forwards the event to the subscriber who subscribes to the Subject object, so that the subscriber can handle the corresponding event.

Finally, there is the operation of unsubscribing. In RxJava, the subscription operation returns a Subscription object to unsubscribe at the right time to prevent memory leaks. If a class produces multiple Subscription objects, we can store it with one CompositeSubscription to cancel subscriptions in batches.

RxBus has many implementations, such as:

AndroidKnife/RxBus (https://github.com/AndroidKnife/RxBus)

Blankj/RxBus (https://github.com/Blankj/RxBus)

In fact, as mentioned earlier, the principle of RxBus is so simple that we can write an implementation of RxBus ourselves:

RxBus implementation based on RxJava1:

Public final class RxBus {private final Subject bus; private RxBus () {bus = new SerializedSubject (PublishSubject.create ());} private static class SingletonHolder {private static final RxBus defaultRxBus = new RxBus ();} public static RxBus getInstance () {return SingletonHolder.defaultRxBus;} / * * send * / public void post (Object o) {bus.onNext (o) } / * * is there an Observable subscription * / public boolean hasObservable () {return bus.hasObservers ();} / * * converted to a specific type of Obserbale * / public Observable toObservable (Class type) {return bus.ofType (type);}}

RxBus implementation based on RxJava2:

Public final class RxBus2 {private final Subject bus; private RxBus2 () {/ / toSerialized method made bus thread safe bus = PublishSubject.create () .toSerialized ();} public static RxBus2 getInstance () {return Holder.BUS;} private static class Holder {private static final RxBus2 BUS = new RxBus2 ();} public void post (Object obj) {bus.onNext (obj) } public Observable toObservable (Class tClass) {return bus.ofType (tClass);} public Observable toObservable () {return bus;} public boolean hasObservers () {return bus.hasObservers ();}}

Introduce the idea of LiveDataBus

Start with LiveData

LiveData is the framework proposed by Android Architecture Components. LiveData is a data holding class that can be observed and can perceive and follow the life cycle of components such as Activity, Fragment, or Service. It is precisely because LiveData is aware of the life cycle of a component that it is possible to update UI data only when the component is in a lifecycle active state.

LiveData requires an observer object, typically a concrete implementation of the Observer class. When the observer's life cycle is in STARTED or RESUMED state, LiveData notifies the observer of data changes; when the observer is in other states, even if the LiveData data changes, it does not notify.

Advantages of LiveData

UI is consistent with real-time data because LiveData is in observer mode so that you can be notified and update UI when the data changes.

To avoid memory leaks, the observer is bound to the life cycle of the component, and when the bound component is destroyed (destroy), the observer automatically cleans up its own data immediately.

There will be no more crashes caused by the Activity in the stop state, for example, when the Activity is in the background state, no LiveData events will be received.

Instead of solving the problems caused by the lifecycle, LiveData can sense the lifecycle of the bound component and notify data changes only when it is active.

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

Solve the Configuration Change problem, rotate the screen or be recycled and start again, and the latest data will be received immediately.

Talk about Android Architecture Components

The core of Android Architecture Components is Lifecycle, LiveData, ViewModel and Room, through which you can elegantly interact with the interface, do some persistent operations, highly decouple, automatically manage the life cycle, and do not have to worry about memory leaks.

Room

A powerful SQLite object mapping library.

ViewModel

An object used to provide data for UI components that can survive changes in the configuration of the device.

LiveData is a lifecycle-aware, observable data container that stores data and alerts when it changes.

Lifecycle

Contains LifeCycleOwer and LifecycleObserver, which are lifecycle owners and lifecycle perceptors, respectively.

Characteristics of Android Architecture Components

Data-driven programming

It is always the data that changes, and the interface does not need to be changed.

Perceiving life cycle to prevent memory leaks

Highly decoupled

The data and interface are highly separated.

Data persistence

Data and ViewModel are not linked to the life cycle of UI and will not be destroyed because of the reconstruction of the interface.

Key: why to use LiveData to build data communication bus LiveDataBus

Reasons for using LiveData

The observability and lifecycle awareness of LiveData make it very suitable to be the basic component of Android communication bus.

The consumer does not need to display the call to the anti-registration method.

Because LiveData is lifecycle aware, LiveDataBus only needs to call the registration callback method, not the displayed call to the anti-registration method. The benefit of this is not only to write less code, but also to completely eliminate the risk of memory leaks caused by other communication bus class frameworks (such as EventBus, RxBus) forgetting to call anti-registration.

Why use LiveDataBus instead of EventBus and RxBus

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 LiveData of the official Android Architecture Components component of Android, and has no other dependencies, and its own implementation has only one class. For comparison, EventBus JAR package size is 57kb Magi RxBus depends on RxJava and RxAndroid, where RxJava2 package size 2.2MB Magi RxJava 1 package size 1.1MB Magi RxAndroid package size 9kb. Using LiveDataBus can greatly reduce the size of the APK package.

The LiveDataBus relying party supports better. LiveDataBus only relies on the LiveData of the official Android Architecture Components component of Android, and the relying party supports better than the RxJava and RxAndroid that RxBus depends on.

LiveDataBus has lifecycle awareness, LiveDataBus has lifecycle awareness, and callers in Android systems do not need to call anti-registration, which is more convenient to use than EventBus and RxBus, and there is no risk of memory leakage.

Design and architecture of LiveDataBus

The composition of LiveDataBus

Message

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

Message channel

LiveData acts as a 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 name.

Message bus

The message bus is implemented through a singleton, and different message channels are stored in a HashMap.

Subscription

The subscriber gets the message channel through getChannel and then calls observe to subscribe to the message for this channel.

Publish

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

LiveDataBus schematic diagram

The realization of LiveDataBus

The first implementation:

Public final class LiveDataBus {private final Map bus; private LiveDataBus () {bus = new HashMap ();} private static class SingletonHolder {private static final LiveDataBus DATA_BUS = new LiveDataBus ();} public static LiveDataBus get () {return SingletonHolder.DATA_BUS } public MutableLiveData getChannel (String target, Class type) {if (! bus.containsKey (target)) {bus.put (target, new MutableLiveData ());} return (MutableLiveData) bus.get (target);} public MutableLiveData getChannel (String target) {return getChannel (target, Object.class);}}

With just 20 lines of code, it realizes all the functions of a communication bus, has lifecycle awareness, and is very easy to use:

Register for subscription:

LiveDataBus.get () .getChannel ("key_test", Boolean.class) .observe (this, new Observer () {@ Override public void onChanged (@ Nullable Boolean aBoolean) {}})

Send a message:

LiveDataBus.get () .getChannel ("key_test") .setValue (true)

We sent an event named "key_test" with a value of true.

At this time, the subscriber will receive the message and deal with it accordingly, which is very simple.

Problems arise

For the first version of LiveDataBus implementation, we found that in the process of using this LiveDataBus, subscribers will receive messages published before the subscription. This is unacceptable for a message bus. Regardless of EventBus or RxBus, subscribers do not receive messages sent before the subscription. For a message bus, LiveDataBus must solve this problem.

Analysis of problems

How to solve this problem? First analyze the reasons:

When the state of LifeCircleOwner changes, the activeStateChanged function of LiveData.ObserverWrapper is called. If the state of ObserverWrapper is active, dispatchingValue of LiveData is called.

In the dispatchingValue of LiveData, the considerNotify method of LiveData is called again.

In the considerNotify method of LiveData, the logic in the red box is the key. If the mLastVersion of ObserverWrapper is less than the mVersion of LiveData, the onChanged method of mObserver will be called back. For each new subscriber, its version is-1 once its version is greater than-1 (each LiveData setting will increase its version by 1), this will cause the LiveDataBus to receive a callback every time a new subscriber registers, even if the action of this setting occurs before the subscription.

Summary of the causes of the problem

For this problem, summarize the core reason why it happened. For LiveData, its initial version is-1, and when we call its setValue or postValue, its vesion will be + 1; for each observer's encapsulated ObserverWrapper, its initial version is also-1, that is, for each newly registered observer, its version is-1; when LiveData sets this ObserverWrapper, if the version of LiveData is greater than the version,LiveData of ObserverWrapper, the current value will be forced to be pushed to Observer.

How to solve this problem

After understanding the cause of the problem, let's see how to solve the problem. Obviously, according to the previous analysis, you only need to set Wrapper's version to be the same as LiveData's version when registering a new subscriber.

So how do you do that? take a look at LiveData's observe method, which creates a LifecycleBoundObserver,LifecycleBoundObserver that is a derivative of ObserverWrapper in step 1. The LifecycleBoundObserver is then placed in a private Map container mObservers in step 2. Both ObserverWrapper and LifecycleBoundObserver are private or package visible, so the version of LifecycleBoundObserver cannot be changed by inheritance.

So can I get the LifecycleBoundObserver from the Map container mObservers and then change the version? The answer is yes. By looking at the source code of SafeIterableMap, we find that there is a get method for protected. Therefore, when we call observe, we can get the LifecycleBoundObserver through reflection, and then set the version of LifecycleBoundObserver to be the same as LiveData.

For the non-lifecycle-aware observeForever approach, the implementation idea is the same, but the specific implementation is slightly different. In the case of observeForever, the generated wrapper is not LifecycleBoundObserver, but AlwaysActiveObserver (step 1), and we have no opportunity to change the version of AlwaysActiveObserver after the observeForever call is completed, because in the body of the observeForever method, the callback occurs in the statement in step 3.

So how to solve this problem for observeForever? Since the callback is within the call, we can write an ObserverWrapper that wraps the real callback. Pass the ObserverWrapper to observeForever, so we check the call stack during the callback, and if the callback is caused by the observeForever method, the real subscriber is not called back.

The final realization of LiveDataBus

Public final class LiveDataBus {private final Map bus; private LiveDataBus () {bus = new HashMap ();} private static class SingletonHolder {private static final LiveDataBus DEFAULT_BUS = new LiveDataBus ();} public static LiveDataBus get () {return SingletonHolder.DEFAULT_BUS } public MutableLiveData with (String key, Class type) {if (! bus.containsKey (key)) {bus.put (key, new BusMutableLiveData ());} return (MutableLiveData) bus.get (key);} public MutableLiveData with (String key) {return with (key, Object.class);} private static class ObserverWrapper implements Observer {private Observer observer Public ObserverWrapper (Observer observer) {this.observer = observer;} @ Override public void onChanged (@ Nullable T t) {if (observer! = null) {if (isCallOnObserve ()) {return;} observer.onChanged (t) }} private boolean isCallOnObserve () {StackTraceElement [] stackTrace = Thread.currentThread () .getStackTrace () If (stackTrace! = null & & stackTrace.length > 0) {for (StackTraceElement element: stackTrace) {if ("android.arch.lifecycle.LiveData" .equals (element.getClassName ()) & & "observeForever" .equals (element.getMethodName () {return true } return false;}} private static class BusMutableLiveData extends MutableLiveData {private Map observerMap = new HashMap (); @ Override public void observe (@ NonNull LifecycleOwner owner, @ NonNull Observer observer) {super.observe (owner, observer); try {hook (observer) } catch (Exception e) {e.printStackTrace ();} @ Override public void observeForever (@ NonNull Observer observer) {if (! observerMap.containsKey (observer)) {observerMap.put (observer, new ObserverWrapper (observer));} super.observeForever (observerMap.get (observer)) } @ Override public void removeObserver (@ NonNull Observer observer) {Observer realObserver = null; if (observerMap.containsKey (observer)) {realObserver = observerMap.remove (observer);} else {realObserver = observer;} super.removeObserver (realObserver) } private void hook (@ NonNull Observer observer) throws Exception {/ / get wrapper's version Class classLiveData = LiveData.class; Field fieldObservers = classLiveData.getDeclaredField ("mObservers"); fieldObservers.setAccessible (true); Object objectObservers = fieldObservers.get (this); Class classObservers = objectObservers.getClass (); Method methodGet = classObservers.getDeclaredMethod ("get", Object.class) MethodGet.setAccessible (true); Object objectWrapperEntry = methodGet.invoke (objectObservers, observer); Object objectWrapper = null; if (objectWrapperEntry instanceof Map.Entry) {objectWrapper = ((Map.Entry) objectWrapperEntry). GetValue ();} if (objectWrapper = = null) {throw new NullPointerException ("Wrapper can not be bull!") } Class classObserverWrapper = objectWrapper.getClass (). GetSuperclass (); Field fieldLastVersion = classObserverWrapper.getDeclaredField ("mLastVersion"); fieldLastVersion.setAccessible (true); / / get livedata's version Field fieldVersion = classLiveData.getDeclaredField ("mVersion"); fieldVersion.setAccessible (true); Object objectVersion = fieldVersion.get (this) / / set wrapper's version fieldLastVersion.set (objectWrapper, objectVersion);}

Register for subscription:

LiveDataBus.get () .with ("key_test", String.class) .observe (this, new Observer () {@ Override public void onChanged (@ Nullable String s) {}})

Send a message:

LiveDataBus.get () .with ("key_test") .setValue (s); on how to use LiveDataBus instead of RxBus to share here, I hope the above content can be of some help to you, can learn more knowledge. If you think the article is good, you can share it for more people to see.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report