GreenRobot EventBus receives mistaken event - android

I'm trying to use EventBus in my project and i have a problem.
I have a super class for fragments with generic EVENT parameter:
public abstract class BaseNetworkFragment<EVENT extends BaseEvent> extends Fragment {
//some code
#Override
public void onResume() {
super.onResume();
EventBus.getDefault().register(this);
}
#Override
public void onPause() {
super.onPause();
EventBus.getDefault().unregister(this);
}
#Subscribe
public void onApiResponse(EVENT event) {
//some action
}
}
I have a super class BaseEvent.
And 2 event classes:
public class EventOne extends BaseEvent{
}
public class EventTwo extends BaseEvent{
}
I create Fragment with generic parameter EventOne and call for Api:
public class MyFragment extends BaseNetworkFragment<EventOne> {
//some code
//make request for Api, when ServiceHelper has results it posts EventOne
ServiceHelper.getInstance().getSomeData();
}
Now i don't use EventTwo and everything works correctly.
But if i add im MyFragment code:
#Subscribe
public void onEventTwo(EventTwo event) {
//some action
}
And call for Api Services, which posts EventTwo as a result, i have mistakes.
My method onEventTwo(); works correctly, but method onApiResponse(); from superclass also receives EventTwo, but it can receive only EventOne! So i have ClassCastException
I also noticed, that if i remove method onApiResponse() from superclass and write it in MyFragment everything will be ok, but i need this method in superclass.
I think that problem is in generic parameter, but i can't fix it.
Also i use retrofit for asynchronous requests.
Please help me)

Generics and type erasure seems to be the problem.
#Subscribe
public void onApiResponse(EVENT event) {
//some action
}
defined in your generic abstract class is really
#Subscribe
public void onApiResponse(BaseEvent event) {
//some action
}
Due to event inheritance being true by default, posts to EventOne are also posted to its superclass BaseEvent (and onApiResponse). You can fix this disabling event inheritance in your eventbus. Assuming you're using the default eventbus, this can be done by
EventBus.builder().eventInheritance(false).installDefaultEventBus()
before the first usage of EventBus.getDefault()

Related

Can I register an Otto bus on both base and child class?

I'm using Otto event bus in my Android app. I've read the GitHub documentation and various questions posted online about how hierarchy traverse is working:
"Registering will only find methods on the immediate class type. Unlike the Guava event bus, Otto will not traverse the class hierarchy and add methods from base classes or interfaces that are annotated"
I understand if I register a bus on a child class, then methods from the base class will not be added. So my question is, can I register a bus in a child class and register another bus in the base class?
public class BaseActivity extends Activity
...
baseBus.register(this);
#Subscribe public void baseAnswerAvailable(BaseAnswerAvailableEvent event) {
// TODO: React to the event somehow in the base class
}
public class MainActivity extends BaseActivity
...
bus.register(this);
#Subscribe public void answerAvailable(AnswerAvailableEvent event) {
// TODO: React to the event somehow
}
Will both of the baseAnswerAvailable and answerAvailable methods get called?
the answer is yes actually, and here is the way
https://github.com/square/otto/issues/26#issuecomment-33891598
public class ParentActivity extends Activity {
protected Object busEventListener;
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
busEventListener = new Object() {
#Subscribe
public void onReceiveLoginEvent(final LoginEvent event) {
ParentActivity.this.onReceiveLoginEvent(event);
}
#Subscribe
public void onReceiveLogoutEvent(final LogoutEvent event) {
ParentActivity.this.onReceiveLogoutEvent(event);
}
};
BusProvider.getInstance().register(busEventListener);
}
//subclasses extend me. This can be abstract, if necessary.
protected void onReceiveLoginEvent(final LoginEvent event) {
Log.d("Tag", "LoginEvent");
}
//subclasses extend me. This can be abstract, if necessary.
protected void onReceiveLogoutEvent(final LogoutEvent event) {
Log.d("Tag", "LogoutEvent");
}
#Override
protected void onDestroy() {
super.onDestroy();
BusProvider.getInstance().unregister(busEventListener);
}
}
Don't usually answer my unanswered questions, but because someone upvoted my question I felt it would help. I tried producing events for both child and base class:
#Produce
public BaseAnswerAvailableEvent baseAnswerAvailableEvent() {
return new BaseAnswerAvailableEvent(message);
}
#Produce
public AnswerAvailableEvent answerAvailableEvent() {
return new AnswerAvailableEvent(message);
}
The answer is no, you cannot produce and subscribe to separate events in a base and child class. I produced both events and only the AnswerAvailableEvent in the child class received an event.

Android EventBus and base class

I am trying to implement some common logic and reaction to some events in base class of all my dialogues.
And registering and unregistering in EventBus, and catching some events in base class.
So when I tried to instantiate an instance of derived class - EventBus throws an exception that DerivedClass has no methods like onEvent(*).
I don't want to add some stub onEvent methods in every derived class, it is not the way software development should be.
It is so sad, if there is no way to use such approach about inheritance.
Did someone faced that?
You could make a protected method(or abstract class with abstract method) in the base class that you could override in child class(if needed), before registering EvenBus.
public class Test extends Fragment{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(doIneedEventBus()){
EventBus.getDefault().register(this);
}
}
#Override
public void onDestroy() {
super.onDestroy();
if(doIneedEventBus()){
EventBus.getDefault().unregister(this);
}
}
protected boolean doIneedEventBus() {
return true;
}
}
Child class:
public class TestChild extends Test {
#Override
protected boolean doIneedEventBus() {
return false;
}
}
Second option:
try {
EventBus.getDefault().register(this);
} catch (Throwable t){
t.printStackTrace();
}
Or you could wait until this issue is fixed in the library -
https://github.com/greenrobot/EventBus/issues/58
Use the rxbus2 library, which is compatible with base classes.
https://github.com/warrenth/RxBus2

Registering a presenter class to EventBus (android MVP)

I'm using greenrobot's EventBus in my android apps and I absolutely like it.
However, now I'd like to seperate the logic from my fragments by using presenters (MVP).
Is the following possible and is it useful?
Fragment:
public class MyFragment implements IMyFragment {
IMyPresenter mPresenter;
#Override
public View onCreateView(...) {
mPresenter = new MyPresenter(this);
}
#Override
public void onResume() {
// EventBus.getDefault().register(mPresenter); // register presenter to bus
mPresenter.resume();
}
#Override
public void onPause() {
// EventBus.getDefault().unregister(mPresenter); // unregister presenter from bus
mPresenter.pause();
}
#Override
public void doSomething() { // gets called via presenter
// ...
}
}
Presenter:
public class MyPresenter implements IMyPresenter {
IMyFragment mFragment;
// constructor to inject fragment
public MyPresenter(IMyFragment mFragment) {
this.mFragment = mFragment;
}
// handle event
public void onEvent(SomeEvent event) {
mFragment.doSomething();
}
public void resume() {
EventBus.getDefault.register(this);
}
public void pause() {
EventBus.getDefault.unregister(this);
}
}
Does this make sense?
Or is it even dangerous regarding unregistering the presenter from the bus and the complex fragment lifecycle?
Edit: Moved bus registration to presenter itself (Thanks to Nicklas).
Any more comments on this architecture?
You're putting too much responsibility on the View. What you want to do instead is have your Presenter expose a resume() and pause() method, and call those in your View. In those methods you'll register() and unregister() on the EventBus.
This puts all the event-handling code in your Presenter. It also means that you can change the event mechanism you use in your presenter, at any time, without having to change a line of code in your View.
In MVP, the only object you'll want to call non-view-related methods on, from the View, is the associated Presenters.

Writing to a TextView outside of a Fragment

I am using a TextView inside of a Fragment.
I wish to update this TextView outside of the fragment (but also outside of an activity) from a callback class.
For example the user scrolls, the callback is called somewhere in my package, and I want the fragment view to be updated.
Can anybody explain how to do this? I did use a Local Broadcast Receiver but it wasn't fast enough in its updating.
Eventually looked at Otto but as we had Guava I implemented a singleton eventbus and used Guava publish/subscribe model to pass stuff around.
Otto however looks very similar.
Use Otto: http://square.github.io/otto/
public class UpdateEvent {
private String string;
public UpdateListEvent(String string) {
this.string = string;
}
public String getString() {
return string;
}
}
...
...
public void update() {
SingletonBus.INSTANCE.getBus().post(new UpdateListEvent(editText.getText().toString()));
}
...
public class FragmentA extends Fragment {
#Override
public void onResume() {
super.onResume();
SingletonBus.INSTANCE.getBus().register(this);
}
#Override
public void onPause() {
SingletonBus.INSTANCE.getBus().unregister(this);
super.onPause();
}
#Subscribe
public void onUpdateEvent(UpdateEvent e) {
//do something
}
}
public enum SingletonBus {
INSTANCE;
private Bus bus;
private SingletonBus() {
this.bus = new Bus(ThreadEnforcer.ANY);
}
public Bus getBus() {
return bus;
}
}
EventBus is a nice and elegant way for communication between modules in Android apps. In this way you should register your fragment as a event subscriber, and post a this specific event from other part of your code. Keep in mind that only UI thread can work with Views.
I don't exactly understand what you want to achieve and why BroadcastReceiver does not work for you, but you may either:
1) try using callbacks (if it is possible in your app design);
2) try using this or that event bus implementation;
Both would work pretty fast without much overhead, compared to broadcasting.
In case 2 you won't have to maintain callback dependencies/references.

Android - event listener

I hope this will be simple question.
I have main activity, on this activity I create an instance of some class. How to send some event form one class to main one? How to setup some kind a listener to send notifications between classes. Only option what I know/use right now is to keep reference to parent class and call directly some function from child class.
I'm wonder if it possible to create something like is in ActionScript, where I can call to dispatchEvent(new Event("name")) and later setup addEventlistener("name" function) ??
If "I implement some class" means that you have declared a nested class inside your Activity class than nested non-static class will have a reference to parent class object.
In general, you can always create dispatcher/listener pattern your self. Create listener interface and add either addListener or setListener method to class that will dispatch event.
Example of listener:
public interface IAsyncFetchListener extends EventListener {
void onComplete(String item);
void onError(Throwable error);
}
Example of event dispatcher:
public class FileDownloader {
IAsyncFetchListener fetchListener = null;
...
private void doInBackground(URL url) {
...
if (this.fetchListener != null)
this.fetchListener.onComplete(result);
}
public void setListener(IAsyncFetchListener listener) {
this.fetchListener = listener
}
}
Example of class with event listener:
public class MyClass {
public void doSomething() {
FileDownloader downloader = new FileDownloader();
downloader.setListener(new IAsyncFetchListener() {
public void onComplete(String item) {
// do something with item
}
public void onError(Throwable error) {
// report error
}
});
downloader.start();
}
}
Just implement a listener(or a list of listeners) on the class that generates the events.
When an event is generated iterate over this list and call a method that all the listeners must implement (via an interface maybe?)
Hope it helped,
JQCorreia

Categories

Resources