Continue RxBus--RxJava Implementing Event Bus

Keywords: network Android Programming Fragment


Hello, everyone. My name is Stone.

Preface

Reasons for Event Bus: In order to make communication between components simple, deep decoupling!
To put it bluntly is to cut off the direct link between components and adopt the publish/subscribe mode (observer mode)

I believe many of us have used it. EventBus perhaps Otto To be the event bus in our APP, so we will be confused, can RxBus really replace EventBus?
Then let's start with the analysis.

This project is deprecated in favor of RxJava and RxAndroid. These projects permit the same event-driven programming model as Otto, but they're more capable and offer better control of threading.
The project has been completed. RxJava and RxAndroid Replace. Rx projects allow event-driven programming models similar to Otto, which are more powerful and easier to operate threads.

Otto has stopped developing, so we just need to compare EventBus with RxBus.

To compare EventBus with RxBus, we need to understand what functions a perfect event bus should have.

  • Easy Subscribe Events: Event Subscribers just declare themselves and are automatically called when an event occurs. Subscription and cancellation can be easily bound to the life cycle of Activity and Fragment.

  • Easy to send events: Event senders just send them directly, regardless of anything else.

  • Convenient switching threads: Some things must be the main thread trunk, and some things must not be the main thread trunk, so this should be clear.

  • Performance: As applications grow, buses may be heavily used, and performance must be good.

Friends of EventBus or RxBus can refer to this article. Can RxBus really replace EventBus?

Next we'll start our RxBus Tour - -------------------------------------------------------------------------------------------------------------

1. Adding RxJava and RxAndroid dependencies

//RxJava and RxAndroid
compile 'io.reactivex:rxandroid:1.1.0'
compile 'io.reactivex:rxjava:1.1.0'

By the way, we use the version of rxjava1.X, and now we have the version of rxjava2.x. There are some differences between them. Interested friends can go and see.

Establishment of RxBus Classes

import java.util.HashMap;

import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action1;
import rx.schedulers.Schedulers;
import rx.subjects.PublishSubject;
import rx.subjects.SerializedSubject;
import rx.subscriptions.CompositeSubscription;

/**
 * Created by shitou on 2017/4/26.
 */

public class RxBus {
    private static volatile RxBus mInstance;
     /**
     * PublishSubject Only data from the original Observable will be sent to the observer after the time of the subscription.
     */
    private SerializedSubject<Object, Object> mSubject;
    private HashMap<String, CompositeSubscription> mSubscriptionMap;

    private RxBus() {
        mSubject = new SerializedSubject<>(PublishSubject.create());
    }

    public static RxBus getInstance() {
        if (mInstance == null) {
            synchronized (RxBus.class) {
                if (mInstance == null) {
                    mInstance = new RxBus();
                }
            }
        }
        return mInstance;
    }

    /**
     * Send events
     */
    public void post(Object o) {
        mSubject.onNext(o);
    }

    /**
     * Is there an observer subscription?
     */
    public boolean hasObservers() {
        return mSubject.hasObservers();
    }

    /**
     * A default subscription method
     */
    public <T> Subscription doSubscribe(Class<T> type, Action1<T> next, Action1<Throwable> error) {
        return toObservable(type)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(next, error);
    }

    /**
     * Returns an Observable instance of the specified type
     */
    public <T> Observable<T> toObservable(final Class<T> type) {
        return mSubject.ofType(type);
    }

    /**
     * Save subscription after subscription
     */
    public void addSubscription(Object o, Subscription subscription) {
        if (mSubscriptionMap == null) {
            mSubscriptionMap = new HashMap<>();
        }
        String key = o.getClass().getName();
        if (mSubscriptionMap.get(key) != null) {
            mSubscriptionMap.get(key).add(subscription);
        } else {
            CompositeSubscription compositeSubscription = new CompositeSubscription();
            compositeSubscription.add(subscription);
            mSubscriptionMap.put(key, compositeSubscription);
        }
    }

    /**
     * unsubscribe
     */
    public void unSubscribe(Object o) {
        if (mSubscriptionMap == null) {
            return;
        }

        String key = o.getClass().getName();
        if (!mSubscriptionMap.containsKey(key)){
            return;
        }
        if (mSubscriptionMap.get(key) != null) {
            mSubscriptionMap.get(key).unsubscribe();
        }

        mSubscriptionMap.remove(key);
    }
}

In RxJava, there is a Subject class, which inherits Observable class and implements the Observer interface, so Subject can play the role of both subscriber and subscriber. Here we use PublishSubject, a subclass of Subject, to create a Subject object (PublishSubject will receive events immediately only after being subscribed). Send to Subscribers)
In Rxjava, the subscription operation returns a Subscription object to cancel the subscription at the right time to prevent memory leaks. If a class generates multiple Subscription objects, we can store it in a Composite Subscription for bulk unsubscribation.

Because the Subject class is non-thread-safe, we convert the PublishSubject into a thread-safe Subject object through its subclass SerializedSubject.

public <T> Observable<T> toObservable(final Class<T> type) {
        return mSubject.ofType(type);
    }

The ofType() method filters out unqualified event types (for example, if your type is EventType 1. class, you can only output EventType 1. class type), and then converts the event types satisfying the conditions into Observable objects of the corresponding type through the cast() method, which is converted in the source code.

/**
 * A default subscription method
 */
    public <T> Subscription doSubscribe(Class<T> type, Action1<T> next, Action1<Throwable> error) {
        return toObservable(type)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(next, error);
    }

The above method encapsulates the subscription method and specifies the thread of execution. We only need to pass in type (event type), next (successful Action1), error (wrong Action1). In fact, you can also encapsulate your own doSubscribe method according to your own needs to simplify the code.

The post() method is called where events need to be sent, indirectly through mSubject.onNext(o); events are sent to subscribers.
At the same time, RxBus provides addSubscription() and unSubscribe() methods to save the `Subscription object'returned when subscribing, and to cancel the subscription, respectively.

Actual War I

The main thread (UI thread) sends String-type events

button click event code

mButton1 = (Button) findViewById(R.id.button);
mButton1.setOnClickListener(new View.OnClickListener() {
     @Override
     public void onClick(View v) {
         //Sending String-type events in the main thread
         RxBus.getInstance().post("hello RxBus!");
     }
});

Implement the following code in onCreate

Subscription subscription = RxBus.getInstance()
                .toObservable(String.class)  
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<String>() {
                    @Override
                    public void call(String s) {
                        mTextView.setText("Received event content"+s);
                    }
                }, new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable) {
                        Log.e(TAG, "error");
                    }
                });

We can then save the subscription object to the HashMap < String, Composite Subscription > collection.

 RxBus.getInstance().addSubscription(this,subscription);

So when we click on the button, textview receives the message.

Finally, it's important to remember to cancel subscription events at the end of the lifecycle to prevent memory leaks that RxJava may cause.

protected void onDestroy() {
        super.onDestroy();
        RxBus.getInstance().unSubscribe(this);
    }

Actual War II

Sending Integer-type events in sub-threads

button click event code

mButton2 = (Button) findViewById(R.id.button2);
mButton2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        RxBus.getInstance().post(1234);
                    }
                }).start();
            }
        });

Implement the following code in onCreate
>

Subscription subscription1 = RxBus.getInstance()
                .doSubscribe(Integer.class, new Action1<Integer>() {
                    @Override
                    public void call(Integer integer) {
                        mTextView.setText("Received event content"+integer);
                    }
                }, new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable) {
                        Log.e(TAG, "error");
                    }
                });

We can then save the subscription object to the HashMap < String, Composite Subscription > collection.

RxBus.getInstance().addSubscription(this,subscription1);

Finally, it's important to remember to cancel subscription events at the end of the lifecycle to prevent memory leaks that RxJava may cause.

protected void onDestroy() {
        super.onDestroy();
        RxBus.getInstance().unSubscribe(this);
    }

Above all are the basic data types sent, so can we send our own encapsulated types? The answer is: yes!

Actual War III

Create the event class you want to send

Let's create a student class: Student Event

public class StudentEvent {
    private String id;
    private String name;

    public StudentEvent(String id, String name) {
        this.id = id;
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Send events

RxBus.getInstance().post(new StudentEvent("110","Xiao Ming"));

Registration and Receiving Events

Subscription subscription2 = RxBus.getInstance()
                .toObservable(StudentEvent.class)
                .observeOn(Schedulers.io())
                .subscribeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<StudentEvent>() {
                    @Override
                    public void call(StudentEvent studentEvent) {
                        String id = studentEvent.getId();
                        String name = studentEvent.getName();
                        mTextView.setText("Student's id:"+id+" Name:"+name);
                    }
                }, new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable) {

                    }
                });

Finally, it's important to remember to cancel subscription events at the end of the lifecycle to prevent memory leaks that RxJava may cause.

protected void onDestroy() {
        super.onDestroy();
        RxBus.getInstance().unSubscribe(this);
    }

Actual War IV

In the broadcast, events are sent and subscribed according to the mode of actual combat.
Define a broadcast that detects the state of the network:

public class NetworkChangeReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = manager.getActiveNetworkInfo();
        if (networkInfo != null && networkInfo.isAvailable()) {
            RxBus.getInstance().post("Network Connection Successful");
        } else {
            RxBus.getInstance().post("Network unavailability");
        }
    }
}

Send prompt events when the network is available or unavailable, and register broadcasts in the onCreate() method:

private void registerReceiver() {
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
        mReceiver = new NetworkChangeReceiver();
        registerReceiver(mReceiver, intentFilter);
    }

Finally, don't forget to cancel the registration and subscription of broadcasts in onDestory().

protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(mReceiver);
        RxBus.getInstance().unSubscribe(this);
}

Here we have implemented several event transmissions, but careful children's shoes may find that we subscribe to events first and then send them (because we use the Publish Subject, which only sends data from the original Observable to the observer after the point at which the subscription takes place, which is in the case above. As we mentioned earlier, if we conversely send events first and then subscribe, how can we ensure that the events sent are not lost? That is the StickyEven function in EventBus. RxBus -- Support for Sticky Events There are four implementations of Subject, which can be visited by interested friends.

Finally, some RxJava learning resources are recommended: Introduction to RxJava,RxJava for Android developers

Posted by Daniello on Thu, 04 Jul 2019 14:29:05 -0700