one object if injected into 2 subcomponents under same custom scope, every time new instance is created of that object

219 views Asked by At

one object if injected into 2 subcomponents under same custom scope, every time new instance is created of that object. I want same instance to be passed to all subcomponents

this is the module

@CustomScope
@Module
public class EventBusModule {

    PublishSubject<Boolean> bus = PublishSubject.create();

    @CustomScope
    @Provides
    public PublishSubject<Boolean> provideRxBus() {
        return bus;
    }
}

these are my subcomponents

@Module
public abstract class ActivityBindingModule {

    @CustomScope
    @ContributesAndroidInjector(modules = {HomeActivityModule.class, 
    EwayBillFragmentProvider.class, EventBusModule.class})
    abstract HomeActivity mainActivity();

    @CustomScope
    @ContributesAndroidInjector(modules = 
    {EwayBillDetailActivityModule.class, EventBusModule.class})
    abstract EwayBillDetailActivity ewayBillDetailActivity();
}

these subcomponents are written inside ActivityBindingModule which is added to my application component. Now I want same instance of my PublishSubject object in both the subcomponents, I am fairly new to dagger and I want to know what am I doing wrong?

1

There are 1 answers

2
Jeff Bowman On BEST ANSWER

You'll need to move your bus into Application scope, which typically means annotating it with @Singleton (if that's how you've annotated your top-level component that ActivityBindingModule is installed into). You'll also need to move your method into a Module installed on that component, which might as well be ActivityBindingModule.

@Module
public abstract class ActivityBindingModule {

  @Singleton
  @Provides
  public PublishSubject<Boolean> provideRxBus() {
    // Dagger stores the instance in your Application component, so you don't have to.
    return PublishSubject.create();
  }

  /* ... your @ContributesAndroidInjector Activity bindings remain here ... */
}

First, an explanation of what you see: @ContributesAndroidInjector creates a subcomponent for each object it annotates, marked with the scope annotations and modules you put on the @ContributesAndroidInjector method and annotation, so that your call to AndroidInjection.inject(this) in onCreate creates a new instance of that subcomponent and uses it to inject the Activity instance.

Your @CustomScope (which may be better-named as @ActivityScope here) on the @Provides PublishSubject<Boolean> method means that your instance will share the same lifecycle as the component that is also annotated with that scope annotation. Here, that's each automatically-generated subcomponent. Furthermore, because your Module is a non-abstract class with public no-arg constructor, Dagger will automatically create a new instance every time it creates a Component that requires your module, which means a different bus for each Activity instance. (It can't and won't do so for Modules that are abstract classes or interfaces.)


You want your bus object to be the same instance between Activities, which means that @CustomScope/@ActivityScope is much too short: You want the object to outlast any single Activity's lifecycle. This means that you'll either need to store the instance elsewhere and pass it into each Activity, or you'll need to store the instance in your Application component itself. I'd recommend the latter, because this is one of the problems Dagger was created to solve, and because this will automatically make the bus available across your application: Dagger subcomponents inherit access to all of the bindings in their parent components. That gives the code you see above. (Note that by doing this, you'll keep the instance of PublishSubject around even when there is no Activity showing, when your application is running in the background; if you want the same instance between Activities, this is a necessary consequence, but choose this carefully to avoid too much background memory use.)

One alternative is that you keep track of the bus instance yourself, and insert it into each Activity. You could do this by having your Module take a parameter, but that is rather tricky to do with dagger.android (which powers @ContributesAndroidInjector). You could also write a @Provides method that delegates to a WeakReference, or use the @Singleton technique above to write a holder that temporarily stores your bus between Activities. However, because Android keeps a lot of control over your transitions between Activities and the Activity lifecycle, it may be the best you can do to keep the bus in @Singleton scope as I did in the code above.