3

I'm using @ContextSingleton to mark singletons that depend on the injection on contexts. From looking at RoboGuice sources and from my own tests it seems however as if it makes a difference between the Application context and different activity contexts. This makes perfectly sense, just that it is - at least for me - problematic when I'm using it together with the event management facility like this:

@ContextSingleton
public class Service {
    @Inject
    private Context context;

    public void doSomething(@Observes MyEvent ev) {
        ...
    }
}

Though the service is defined as singleton, no instance of it seems to be created until it is first injected somewhere, apparently through lazy-loading. So fireing a MyEvent does not make the listener call upon. We thought then we could "manually" eager-load the classes beforehand in our application like

RoboGuice.get(context).getInstance(Service.class);

and therefor get the listeners registered, but this only worked properly when executed within the Activity that later also injected the EventManager to fire the event, but not the application.

So, in an ideal world I'd expect that I could tell RoboGuice to which context it should bind a singleton, much like this

@ContextSingleton(MyApplication.class)
public class Service {
    ...
}

but apparently this is not possible.

What am I missing?

Thomas Keller
  • 5,933
  • 6
  • 48
  • 80

2 Answers2

2

I think You're missing that events don't propogate across contexts and are context specific - see the "Things to be aware of" section in the (old, but still valid) events documentation. So even if your singleton is loaded in the application context, it can't be made aware of events occurring within the context of each activity. I assume they had a good reason for doing it that way, but Maybe if you subclass Application you can inject an EventManager there to be globally accessible.

I haven't tried it so I'm not sure it will work, and at any rate at that point you'd have to be injecting events into a different EventManager, so you might be better off defining an interface for this singleton and work with it that way, since events won't propogate the way you'd like them to.

JRaymond
  • 11,625
  • 5
  • 37
  • 40
  • 1
    Thats a bit of a pity then. I understand that events across activities are a bad idea, since activities might or might not be instantiated and can be destroyed at any tim. But why I cannot specifically scope singletons to the application context, which is guaranteed to stay there all the time, is beyond me. Before I was using RoboGuice I followed the practise to subclass the app and provide getters for all of my "global" objects, and I really thought RoboGuice would fix/untangle that for me. Anyways, making only the event manager globally accessible like this might be better than nothing... – Thomas Keller Sep 26 '12 at 07:18
  • @tommyd you can always write a patch ;) – JRaymond Sep 26 '12 at 15:47
  • 1
    Yeah, I guess so :) If the time permits and if I fully understand what is going on there, I'll have a look at it. – Thomas Keller Sep 26 '12 at 15:51
  • Please note that RG 3 will have a GlobalEventObserver that can propagate events accross contexts – Snicolas Jul 30 '14 at 13:55
2

@JRaymond is right. An instance of a RoboGuice Event listener can't listen to events in more than one context.

But this can be easily solved. Don't make your singleton listen to events directly. Each activity can listen to its own events and then put the singletong in a given state :

public class MyActivity extends RoboActivity {
    @Inject 
    private InsideAppManager mInsideAppManager;

    public void onHandleResumeEvent( @Observes OnResumeEvent onStartEvent ) {
        mInsideAppManager.setInsideApp(true);
    }

    public void onHandlePauseEvent( @Observes OnPauseEvent onStopEvent ) {
        mInsideAppManager.setInsideApp(false);
    }
}

And your singleton :

@Singleton
public class InsideAppManager {
    private boolean isInsideApp;

    public boolean isInsideApp() {
        return isInsideApp;
    }

    public void setInsideApp(boolean isInsideApp) {
        this.isInsideApp = isInsideApp;
    }
}
naXa stands with Ukraine
  • 35,493
  • 19
  • 190
  • 259
Snicolas
  • 37,840
  • 15
  • 114
  • 173
  • Yeah, we basically use constructs like this in a couple places. Just that we named our `InsideAppManager` simply `VolatileState`, as it holds the data across different contexts in a non-persistent way. – Thomas Keller Sep 02 '13 at 10:27