Events being received multiple times - Greenrobot eventbus - android

I am using Greenrobot's EventBus in my app, and it works fine.
However, if I press the back button to close the app, then restart the app instantly I seem to receive the event twice. If I then do so again, I will receive it three times and so on.
I am checking with logs and debugging to see if I have multiple instances of any classes, or if I am registering multiple times, but I can't see any extra classes and using isRegistered returns false.
Any ideas?
Thanks

Are your register/unregister calls paired correctly? E.g. if you register() in Activity.onResume(), are you calling unregister() in Activity.onPause().
Closing all activities does not kill your process. I.e. all registered classes are still there, you have to explicitly clean up and unregister from the event bus, or reuse them when the Activity comes back.

This is old, but just in case anyone has this problem also: Tread lightly when using EventBus inside dynamically generated things like Fragments or other classes; I didn't really understand why they were posting to the EventBus more than once, but I think it had to do with this (I had more than one dynamically generated Fragment). It worked normally once I put the register(), unregister(), onEvent() into the parent Activity code (which conveniently also uses onPause() and onResume()).

Same thing happening in my case when I am using the
EventBus.getDefault().postSticky(new Event("Hii !"));
for sending the event. The event is received multiple times when I come to that activity. So I fixed this by removing the event after receiving in onEvent method. This solved my problem. Used: removeStickyEvent(object)
#Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(Event event) {
/* Do something */
EventBus.getDefault().removeStickyEvent(event);
}

The problem was not that the event was actually fired multiple times, but that the handler was invoked multiple times. As seen in the code above, the bus.register method is called everytime I create the object; because of the activities lifecycle, this happened multiple times, causing the handler to be invoked multiple times.

I had a specific case that I want to share. Maybe it helps someone else.
While we are using a parent activity for all of our Activities in our project, we register and unregister EvenBus for each activity inside the parent class.
In one of our activities, we were calling EventBas before invoking the previous activity's EventBus. Then we had twice trigger

Related

Could I use EventBus in Activities communication?

There are three activities A, B, and C
Register EventBus in Activity A onCreate(),and unregister on onDestroy(), and a method onEvent(TestEvent e);
Activity A starts Activity B
Activity B starts Activity C
In Activity C:
EventBus.getDefault().post(new TestEvent("close A"));
I use EventBus in this way , and it works well. Is there anything wrong in my code ?
It's okay. EventBus is thread safe and has a lot of methods to make it easier to work with, like onEventMainThread, onEventBackgroundThread, onEventAsync.
The thing with your code is this: your activity will continue to get events even if it is in background. And that's okay (in this particular case). If, however, you'll have to implement something else in the future, keep this in mind:
onCreate register -> onDestroy unregister
onStart register -> onStop unregister
onResume register -> onPause unregister
And there's something else, too: you have to make absolutely sure that your activity is only registered ONCE. Because, if you register more than once, you will receive as many events as the number of registers. Thus, please modify your register like this:
if (!EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().register(this);
}
If you need more details, please read more about EventBus here.
However, if you follow these simple rules, your activity's lifecycle. I use it a lot and I don't encounter issues.

Fragment: getActivity() is null after onPause()

I have several Fragments that are hosted by an activity. They register listeners that are called from a custom Application class in onResume() and unregister them in onPause().
The Activity sometimes exchanges Fragments by using fragmentTransition.replace(...)
Sometimes (quite rare) getActivity() that is called in the listeners returns null.
How is that possible? The listener should not be called because he should unregister first?
To force this, you can install my app and click on a cover (calls replace on a new fragment) and turn the device (runtime change) at the same time.
Thanks for the contribution. I fixed this issue. It was caused by a very rare race condition. I locked the accessors correctly, but assumed something wrong, when I used a handler to post to the ui thread. This had a very tiny delay that caused the race condition to fail.
I fixed it by using getActivity().runOnUiThread(...) instead which has no delay.
Sorry for bothering with this very specific problem.

Will EventBus.post() deliver event if configChanges occur or if activity in background?

I read documentation here but there is no clear explanation will event trigger if configChanges occur in activity or if activity in background with EventBus.getDefault().post() . Now I'm using EventBus like this:
EventBus.getDefault.postSticky(new SomeEvent());
public void onEventMainThread(SomeEvent someEvent){
EventBus.getDefault().removeStickyEvent(someEvent);
}
I would like to avoid this boilerplate code.
The Activity will obviously not receive any events between unregister() in onDestroy() and register() in onCreate() - remember that by default the Activity will be fully recreated when configuration changes. However, if you registerSticky() then you'll have access to the latest published event, even if it arrived at the moment of Activity recreation.

Avoid Service callback when Activity gets closed and re-opened

I have a LocalService that exposes a Binder with some APIs. I create a Service Listener, just like this:
if (dataServiceListener == null) {
dataServiceListener = new DataServiceListener();
mainActivity.getApplicationContext().bindService
(new Intent(mainActivity, LocalService.class),
dataServiceListener.svcConn, mainActivity.BIND_AUTO_CREATE);
}
After I call the method that the Binder in dataServiceListener exposes, I get the response in the dataServiceListener onResult() method. Up to this point, no kind of issues, everything is working.
Some sort of problem occurs when I close the Activity that is waiting for the Service Listener callback and immediately reopen it. Even though I re-instantiate the dataServiceListener in onCreate(), I get two callbacks instead of one, the old one from the destroyed Activity and the latter (right) one; this way the results mix up on the UI.
Is there a way to tell the Service or the Service Listener that when the activity finishes, the callbacks must be avoided. Or maybe even destroy the ServiceListener objects.
I think this is the issue that Mark L. Murphy (Commonsware) described in "The Busy Coder's Guide to Android Development":
The biggest catch is to make sure that the activity retracts the listeners when it is done.
How can I do this? Is there a way to get rid of the useless listeners when the activity finishes?
Thank you!
I had the same issue. I was working in a remote sevice using AIDL. I got this problem when i am trying do unregister my listeners using the remove method from ArrayList Collection inside a foreach loop, because I was not using asBinder in the comparision. Searching fora solution, I find out the RemoteCallbackList class in Android API. This class does exactly what i needed, and what i think you should do, on a easy way, taken all reponsabilites for the hard work that involves this task.
From the Android API:
To use this class, simply create a single instance along with your service, and call its register(E) and unregister(E) methods as client register and unregister with your service. To call back on to the registered clients, use beginBroadcast(), getBroadcastItem(int), and finishBroadcast().
Broadcast sample:
int i = callbacks.beginBroadcast();
while (i > 0) {
i--;
try {
callbacks.getBroadcastItem(i).somethingHappened();
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
}
callbacks.finishBroadcast();
The code you show is for binding to a service. You do not show where you are registering a listener with that service. You apparently are, based upon your question and your reference to an onResult() method. Given the nature of your problem, I am going to guess that what you're doing is:
Binding to the service in onCreate()
In onServiceConnected(), you are calling some sort of setListener() method on the Binder
In that case, if we ignore configuration changes, the proper way to unwind matters would be to, in onDestroy(), call some removeListener() method on the Binder, then call unbindService().
Configuration changes, particularly in a pre-fragment world, make this complicated. It's the reason why this sample project (and the accompanying material in the book) is so icky. Binding is twitchy -- if you unbind from the old activity, and nothing else is keeping the service around, the service will shut down before the new activity gets a chance to bind. Binding is also state -- you cannot simply fail to unbind, lest you leak stuff.
So, the recipe becomes:
Bind to the service in onCreate() using the Application Context
In onServiceConnected(), call sort of setListener() method on the Binder
In onRetainNonConfigurationInstance(), make note of the fact that you're undergoing a configuration change, and return some Object that has your Binder, your Listener, and all the rest of your state
In onCreate(), use getLastNonConfigurationInstance() -- if it is null, proceed as normal, but if it is not null, hold onto that Binder and Listener and don't re-bind and re-register the listener
In onDestroy(), if the flag from Step #3 above is false (i.e., we are not undergoing a configuration change), call some removeListener() method on the Binder, then call unbindService().
Using fragments with setRetainInstance(true) can probably simplify this some, though I have not worked through a sample for that yet.
I had this issue too. You need to release all the resources,listeners,threads from the service when it finishes.
Your activity has to register/unregister itself as the listener. You need to use the proper lifecycle callback methods, not onBackPressed(). Register onStart(), unregister onStop(). One way to do it is to make the listener a static member of your service, and provide static register/unregister methods. Then call those from your activity as appropriate.
I finally solved the issue (and no, I haven't been working on it for so long :D).
The callback to the listener was made before the Fragment's onDestroy was called. So the boolean "dontupdate" value was never set to false. Overriding onBackPressed in the main activity solved the problem, as I invoked a destroy() method for each fragment that takes care of setting the boolean value to false.

Which lifecycle event is best to register/unregister listeners?

I have manager classes that take an activity as a listener. I use the managers to do threaded calls, work etc and then call back to the listener(activity) when things are done, need changed and so on.
I want to register and unregister the activity as a listener when it is no longer visible. This will prevent unwanted changes from happening (like dialogs appearing when the activity is no longer visible).
My question is, what lifecycle events are best to do this registering. I started with onPause() and onResume() which worked well except when I had an activity that was doing stuff in onActivityResult(). Since onActivityResult() gets called before onResume() my managers are not always registered in time.
Do I need to register in onResume() AND onActivityResult() or is there a better way to approach this?
An alternative approach may be to postpone the processing currently done in onActivityResult() until after the listeners are registered in onResume().
Possible ways of doing this include posting to the message queue, e.g. using a Handler, setting a Runnable object to be called by onResume, or simply storing the result data received by onActivityResult().
This would also ensure that the activity really has come to the foreground when the listener methods are called.
onResume() and onPause() are the best for this. The onDestroy(), per the documentation, is not guaranteed to be invoked though this is a favorite for many people, so stick with the pauses and resumes.
You can have the handle of the current Activity in the Manager class. Register its presence on onCreate() and unregister it on either onCreate() by some other Activity, or onBackPressed() of the current Activity.
On a related note, I would recommend an MVC (or similar) architecture where the controller has awareness of the view's status (the controller can track the onCreate() and onBackPressed() of each Activity).

Categories

Resources