0

How do I create a singleton object that I can access anywhere from any place in my code?

  • Objects(1) from which I want to inject my signleton component can't be created withing dagger.
  • Those objects don't have common dependencies which I can access (like getApplication()

Quick to an example:

InjectedClass:

public class InjectedClass {
    public InjectedClass() {
        System.out.println("injected class created");
    }
}

HolderClassA:

public class HolderClassA { // this is one of object I marked with (1)
    @Inject InjectedClass b;
    public HolderClassA() {
        Injector build = DaggerInjector.builder().build();
        build.inject(this);
        System.out.println(b);
    }
}

HolderClassB:

public class HolderClassB { // this is another object I marked with (1)
    @Inject InjectedClass b;
    public HolderClassB() {
        Injector build = DaggerInjector.builder().build();
        build.inject(this);
        System.out.println(b);
    }
}

Provider:

@Module
public class Provider {

    @Provides
    @Singleton
    InjectedClass provideInjectedClass() {
        return new InjectedClass();
    }
}

Injector:

@Component(modules = {Provider.class})
@Singleton
public interface Injector {

    void inject(HolderClassA a);
    void inject(HolderClassB a);

}

Somewhere in code:

new HolderClassA();

Somewhere else in code that is NO WAY related to previous code, nor has the same parent or can access same objects (like in dagger guide with getApplication()):

new HolderClassB();

Actual result: two instances of InjectedClass are crated

Expected result: a single instance of InjectedClass is created.

The issue is DaggerInjector.builder().build(); creates different scopes which don't know about each other. For example, I can solve this issue by creating static variable DaggerInjector.builder().build() and call inject from it. But thus it wouldn't be dependency injection anymore, but rather not trivial singleton pattern.

deathangel908
  • 8,601
  • 8
  • 47
  • 81
  • If you have application(Singleton) class in android and build the Injector you should see the same instance. Since you call build in both HolderClassA and HolderClassB you see two different instances. You have already mentioned the same in your last comment. See https://guides.codepath.com/android/dependency-injection-with-dagger-2 – Raghunandan Mar 28 '18 at 14:49
  • I understand why it happens, I don't understand how I decouple them from Application or any other class that could be common. What I expect from Dagger is to generate a class that's provide the same injector. E.g. from HolderClassB `DaggerInjector.commonInstance.inject(this)` – deathangel908 Mar 28 '18 at 14:51
  • Dagger doesn't do that for you. If you can't rely on `Application` you can use a static variable which stores your `Injector` as an workaround. – Christopher Mar 28 '18 at 15:08
  • Damn... Would it be a good practice if I create a static instance of `InjectedClass` inside of `Provider` class with lazy load inside of `provideInjectedClass`? I mean how do I mock this `InjectedClass` e.g. when testing? – deathangel908 Mar 28 '18 at 15:12

1 Answers1

1

From what I understand from comments, you want separate components for your holder classes, that would inject same instance? Pardon me if I am wrong. You can try this.

@Component(modules = arrayOf(AppModule::class))
@Singleton
interface AppComponent {
    //sub components
    fun plusHolderClass1(holderModule: HolderModule1):HolderComponent1
    fun plusHolderClass2(holderModule: HolderModule2):HolderComponent2    
    //provision methods
    fun getInjectedClass():InjectedClass
}

This is your application component, or the top level component, that you initialise in your application, of course using your Module class that will provide the Injected class as a singleton.

@Subcomponent(modules = arrayOf(HolderModule1::class))
@ActivityScope
interface HolderComponent1{
    fun inject(holder:Holder1)
}

And a similar one for Holder2 class. You can define your local scope dependencies in the modules.

But of course even in this case you have to store the instance of appComponent in Application class.

While injecting

appComponent.plusHolderComponent1(HolderModule1()).inject(yourObject)

The InjectedClass object will be injected to yourObject by fetching it from provision methods

Debanjan
  • 2,817
  • 2
  • 24
  • 43
  • Thank you for the response! I greatly appreciate that. But here is the issue, I can't access AppComponent as I noted in my question. I want something like in [spring](https://stackoverflow.com/a/28408260/3872976). Think of my `HolderClassA` as a separate library that I may want to `ctrl+c` `ctrl+v` anywhere w/o breaking dependencies. – deathangel908 Mar 30 '18 at 21:23
  • In case of Spring, they are injecting the Application Context, which is part of Spring package, but in your case InjectedClass can be implemented anywhere in the code, and has to be explicitly mentioned to Dagger. Again, Since Dagger is not a part of Android package, it's not possible(so far by my knowledge) to access and share the same component over different dependencies or application modules. I think you have to change your structure a bit. And may be ask the app using your library to initiate it with an implementation of InjectedClass or Application class, like Facebook SDK does. – Debanjan Mar 31 '18 at 03:44