Event handling with RxBus on Android

Introduction

Up to Last time, I got the data from the API and brought it to the ViewModel. This time, I will explain the part to bring from ViewModel to View, event processing.

Prerequisites

Please refer to here for the prerequisites of the code explained here.

About the need for event processing

You need to notify View (Activity and Fragment) of the result of asynchronous processing (such as api) from ViewModel. At this time, if the return value is received in the synchronous processing, the Main Thread will be frozen. Also, if the ViewModel has a View reference, you need to be aware of the life cycle in the ViewModel, View is not necessary because View is required for unit testing of ViewModel. Therefore, I use an event to receive it only when the View is alive.

Reasons for choosing RxBus.

EventBus is a well-known library for handling events. The reason for using RxBus this time is There was a story that the notification of EventBus was difficult to understand, so I chose RxBus.

This is because EventBus registers the event globally, Looking now, it seems that it depends on the implementation as follows. https://qiita.com/subaru44k/items/27cd4828edc65cd9d7a6

In that sense, EventBus might have been good considering the learning cost, Another advantage of using RxBus is that the libraries used can be integrated with asynchronous processing. There is no reason to use RxBus just because you are using RxJava. RxJava and EventBus are also OK.

I think that this area should be selected according to the situation of the team.

Event mechanism by RxBus

In RxBus, by accessing the same instance of RxBus at the place you use (for example, View and ViewModel), It enables the exchange of events. I try to get an instance of RxBus in a singleton. Event registration and monitoring can now be loosely coupled from anywhere.

Initially, Application had an instance, When I run a unit test, I can't run it without Application, so I made it a singleton. If you run the test with Android Test, it will work, so I wonder if it was okay, I'm using a singleton now.

RxBus and life cycle

When using it in View, if you do not release the registered event along the life cycle, It can leak or result in an error without a reference. I'll explain the details later, but when the Activity or Fragment turns behind or is released, You also need to unmonitor the event.

Sample code

RxBus implementation.

First, define RxBus. The source is here.

    public class RxBus {
        private final PublishSubject<Object> bus = PublishSubject.create();
        private static RxBus rxBus;
    
        private RxBus() {
    
        }
    
        public static RxBus getInstance() {
            if (rxBus == null) {
                rxBus = new RxBus();
            }
            return rxBus;
        }
    
        public void send(final Object event) {
            bus.onNext(event);
        }
    
        public Observable<Object> toObservable() {
            return bus;
        }
    
        public boolean hasObservers() {
            return bus.hasObservers();
        }
    }

Define an instance of PublishSubject that actually registers the event, Defined to return its own instance as a singleton.

Example of sending an event with RxBus

It is a process that actually uses RxBus and passes the result obtained asynchronously from the API to View. The source is here.

    GithubRepositoryApiCompleteEventEntity eventResult = new GithubRepositoryApiCompleteEventEntity();
    eventResult.setListItems(data);
    if (response.getItems() != null && response.getItems().size() > 0) {
            eventResult.setResult(true);
    }
    
    //Call an event
    RxBus.getInstance().send(eventResult);

The acquired data and the acquisition result are stored in the Entity used for exchanging events called GithubRepositoryApiCompleteEventEntity. Specify this instance as an event argument and send the event.

Example of receiving an event with RxBus

It is a process to receive the event sent by RxBus. The source is here.

            this.compositeDisposable = new CompositeDisposable();
            this.compositeDisposable.add(RxBus.getInstance()
                    .toObservable()
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Consumer<Object>() {
                        @Override
                        public void accept(Object obj) {
                            if (obj instanceof GithubRepositoryApiCompleteEventEntity) {
                                //List results
                                if (((GithubRepositoryApiCompleteEventEntity) obj).isResult()) {
                                    RecyclerView list = view.findViewById(R.id.githubRepositoryList);
                                    list.setLayoutManager(new LinearLayoutManager(view.getContext()));
                                    list.setAdapter(new GithubRepositoryListAdapter(((GithubRepositoryApiCompleteEventEntity) obj).getListItems()));
                                    list.setVisibility(View.VISIBLE);
                                    list.addItemDecoration(new DividerItemDecoration(view.getContext(), DividerItemDecoration.VERTICAL));
                                }
                                view.findViewById(R.id.progressBar).setVisibility(View.GONE);
                            }
                            //Do not handle other events
                        }
                    }));
    
            ExpApplication.getInstance().getViewModelLocator().getGithubRepositoryListViewModel().getRepositoryListData();
            
    //~ Omitted ~
            
        @Override
        public void onStop() {
            super.onStop();
            this.compositeDisposable.clear();
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            this.compositeDisposable.clear();
        }

Define an instance of CompositeDisposable and Set RxBus event reception processing to this. By calling CompositeDisposable.clear () with onStop () or onDestroy () and canceling monitoring, It can be processed along the life cycle.

RxBus event reception processing calls .toObservable () and then Specifies the thread to process with .observeOn (AndroidSchedulers.mainThread ()). Since I'm messing with the screen, I'm using Main Thread this time, You can also run it in a thread different from Main with Schedulers.newThread (). .subscribe (new Consumer <Object> () {The specific reception process is described after.

public void accept (Object obj) {The following is the reception process, The argument specified in the transmission process is sent as an Object type variable. ʻIf (obj instanceof GithubRepositoryApiCompleteEventEntity) {`determine the type and It is determined whether it is an event you want to receive. If it is an event to be received, it will be processed.

in conclusion

So, I tried to explain RxBus. Next time, I would like to explain sqLite by room.

Recommended Posts

Event handling with RxBus on Android
Compatible with Android 10 (API 29)
[Android] Notes on xml
Firebase-Realtime Database on Android that can be used with copy
Customize list view on Android
Multipart transmission library on Android
Use serial communication on Android
ROS app development on Android
Use native code on Android
RxJava (RxAndroid) + RxBus + Data Binding + room sample (based on Java1.7) on Android.
How to call with One Touch (without confirmation) on Android (Java)