7
votes

I have a module as follows.

@Module
public class AppModule {
    private final Application app;

    public AppModule(Application app) {
        this.app = app;
    }

    @Provides
    @Architecture.ApplicationContext
    Context provideContext() {
        return app;
    }

    @Provides //scope is not necessary for parameters stored within the module
    public Context context() {
        return provideContext();
    }

    @Singleton
    @Provides
    Application provideApp() {
        return app;
    }

    @Singleton
    @Provides
    SoundsRepository provideSoundsRepository(Context context, SoundsDAO soundsDAO) {
        return new SoundsRepository(context, soundsDAO);
    }
}

A component like this.

@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {

    void inject(Global global);

    void inject(MainActivity mainActivity);


    @Architecture.ApplicationContext
    Context getContext();

    Application getApplication();

    void inject(PostView postView);

    void inject(MediaPlayerService mediaPlayerService);
}

In activity, fragment or service, I do this

@Inject
    SoundsRepository soundsRepository;

@Override
protected void onCreate(...) {
    //....
    ((Global) getApplication()).getComponent().inject(this);
}

In SoundsRepository

@Singleton
public class SoundsRepository {
    @Inject
    public SoundsRepository(Context context, SoundsDAO soundsDAO) {
        this.context = context;
        this.soundsDAO = soundsDAO;
        System.out.println(TAG + "INIT");
    }
    // ....

}

So, now, every time I start to access an activity or service where SoundsRepository is injected, I get a new instance, I mean, the constructor of "SoundsRepository" fires again.

What am I doing wrong?

EDIT : Inject in Application Class

public class Global extends MultiDexApplication {

    protected AppComponent appComponent;
    private boolean calledAlready = false;

    @Override
    public void onCreate() {
        super.onCreate();
        //if (LeakCanary.isInAnalyzerProcess(this)) return;
        //LeakCanary.install(this);
        initFirebasePersistance();
        appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
        appComponent.inject(this);
        FrescoUtil.init(getApplicationContext());
    }


    public AppComponent getComponent() {
        return appComponent;
    }
}
1
Can you please add how you're creating the application component? Meaning, how are you creating the component returned here: ((Global) getApplication()).getComponent()? And when are you destroying it... - Fred
@Relm, as @Fred asked, it's interesting how the implemnetation of Global#getComponentI() looks like. - azizbekian
Damn... Everything looks good. I need another minute here :| - Fred
Man I don't know. Everything seems ok. You're creating the component on application create and you keep it until the application is actually destroyed. This means that everything scoped with singleton should stay a singleton inside of each app run. Maybe double check that you're not seeing a log from a previous app run. Also, put a log or something on the provider method. You might accidentally be calling new SoundsRepository() somewhere else outside of dagger. - Fred
I'd try removing the inject annotation from the constructor, since clearly you're not using constructor injection and see if that fixes it, since that would explain the issue. Also you should think about switching to proper constructor injection and removing the provides method from your module, adding the scope to the top of the class. - David Medenjak

1 Answers

5
votes
  1. In your module you have a method that provides an instance of SoundsRepository - good
  2. In your AppComponent you are missing a:

    SoundsRepository soundsRepository();
    
  3. In your Global which extends Application/MultidexApplication you create your DaggerAppComponent - good

  4. In your other activities/fragments/services just call:

    Global application = (Global) getApplication();
    SoundsRepository sr = application.getComponent().soundsRepository()
    

Android guarantees you have only one instance of your Application (Global) class for all other actvities/services (its somewhat like a singleton).

So keep your component in that application class, and whenever you need your class, call: (YourApplication) getApplication().getComponent().yourSingleInstanceSomething();

I created and tested sample code for you: https://github.com/zakrzak/StackDaggerTest

Dagger's @Singleton is just a scope, and does not guarantee returning a singular instance of a class.

In my understanding, if you:

void inject(PostView postView);

you tell Dagger to make everything you annotated with @Provided in AppModule accessible in your PostView as soon as you request it with:

@Inject
SoundsRepository soundsRepository;

then dagger just calls the @provided method which in your case returns a new SoundRepository instance:

@Singleton
@Provides
SoundsRepository provideSoundsRepository(Context ........) {
    return new SoundsRepository(...);
}

which causes your problem