filipp.kowalski
filipp.kowalski

Reputation: 5605

EventBus only works when put in Handler

I have ViewPager which contains three different Fragments, every fragment has it's own API call. Let's say that first fragment is requesting something from API. Callback is returned, then I am saving it to local Realm database and after that I am sending it with EventBus to Fragment. The problem is that I get No subscribers registered for event class error and nothing happens.

After some time I managed to solve this in this way :

final GetInfoResponse response = new GetInfoResponse(databaseService.getInfo());
new Handler().post(new Runnable() {
                    public void run() {
                        EventBus.getDefault().post(response);
                    }
                });

If I put EventBus in Handler the Event is delivered properly to Fragment.

I was thinking that maybe Realm is switching threads when it gets data from database. But I am not sure. On the other side, if I am not doing local saving and send EventBus straight from REST Callback, everything works fine.

EDIT : GetInfo method

public List<Information> getInfo() {
        List<Information> informationList;
        Realm realm = Realm.getInstance(getConfig(context));
        realm.beginTransaction();
        RealmResults<Information> realmList = realm.where(Information.class).findAll();
        informationList = new ArrayList<>(realmList);
        realm.commitTransaction();
        return informationList;
    }

Why is it so, and how can I do it better?

EDIT 2 : After changes proposed by @EpicPandaForce. This is my code structure and path to call API. I have Fragment. Let's call it InfoFragment. In onCreateView I am doing this :

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.info, container, false);
            realm = Realm.getInstance(DatabaseConfiguration.getConfig(getActivity()));
            DataManager dataManager = new DataManager(realm, getActivity());
            dataManager.getInfo();
            return view;
        }

        public void onEvent(GetFaqCategoriesResponse response) {
        ...
        }

        @Override
        public void onStart() {
            super.onStart();
            EventBus.getDefault().register(this);
        }

        @Override
        public void onStop() {
            EventBus.getDefault().unregister(this);
            super.onStop();
        }

Now, my DataManager looks like this :

public DataManager(Realm realm, Context context) {
        this.context = context;
        databaseService = new DatabaseService(realm);
    }

public void getInfo() {
        if (ConnectionUtils.isNetworkAvailable(context)) {
            ServerService.getInfo(this);
        } else {
            final GetInfoResponse response
                    = new GetInfoResponse(databaseService.getInfo());
            new Handler().post(new Runnable() {
                public void run() {
                    EventBus.getDefault().post(response);
                }
            });
        }
    }

My DatabaseService :

private Realm realm;

public SupportDatabaseService(Realm realm) {
    this.realm = realm;
}

public List<Info> getInfo() {
    RealmResults<Info> realmList = realm.where(Info.class).findAll();
    return new ArrayList<>(realmList);
}

I am closing Realm instance with realm.close() in onDestroyView of InfoFragment.

Upvotes: 1

Views: 759

Answers (1)

EpicPandaForce
EpicPandaForce

Reputation: 81539

To automatically update the Realms on all looper threads, Realm sends a message to the main thread looper's handler to start updating when you commit a transaction. So unless you are subscribed to the Realm's changes in a RealmListener (which I don't recommend as it updates every single time the realm is modified!), you will only receive the notification about it when the next handler cycle runs. If you use onEvent(), then as specified for the default threading mode, your event will be delivered immediately and not through a handler (which sends a runnable to execute on the other looper thread on next iteration).

Basically, if you don't send it through the handler, then the Realm of the other thread won't be updated yet at the time of receiving the event. If you are sending these Realm objects from the main thread to the main thread, you can use onEventMainThread() instead of onEvent() to automatically pass it through a handler to the main thread.

By the way, you are using Realm incorrectly, the Realm instances you open should also be closed, but a Realm object associated with a given Realm can only be accessed on that same thread, and a Realm object can only be accessed from an open realm. You don't need to open a new realm to execute the query, you could just use findAll() on your main thread using the Realm instance you opened in onCreateView() as you also close it in onDestroyView(), and findAll() is executed as a lazy query and therefore isn't memory intensive whatsoever. And you're also beginning and committing a transaction even though you're not actually doing any writes...? :P

Upvotes: 1

Related Questions