Matthew Bahr
Matthew Bahr

Reputation: 347

Dagger 2 Constructor Injection not happening before module called

I'm learning Dagger 2 and am working on an app. I have a settings module which depends on a settings manager which depends on a shared preferences manager. My problem is that my Settings Module is not getting injected with a settings manager before it itself is being called. That settings manager needs a SharedPrefsManager which is also not being injected anywhere.

What am I doing wrong?

Snippets in order of dependency:

@Module
public class SettingsModule {   
    @Inject SettingsManager manager;

    @Provides
    @TimeControl
    int[] provideResetTime(){
        return manager.getResetTime();
    }

    @Provides
    @ThemeControl
    int provideThemeID(){
        return manager.getTheme();
    }
}

Depends on Settings Manager:

public class SettingsManager{
    private SharedPreferencesManager manager;
    @Inject
    SettingsManager(SharedPreferencesManager manager){
        this.manager = manager;
    }
}

Depends on Shared prefs manager:

public class SharedPreferencesManager {
    private static SharedPreferencesManager instance = null;
    public static SharedPreferencesManager getInstance(){return instance;}
    String prefsKey = "SHAREDPREFSKEY";
    SharedPreferences sharedPrefs = null;
    Context applicationContext = null;

    @Inject
    SharedPreferencesManager(@ApplicationContext Context applicationContext){
        this.prefsKey = prefsKey;
        this.applicationContext = applicationContext;
        sharedPrefs = applicationContext.getSharedPreferences(prefsKey,Context.MODE_PRIVATE);
        instance = this;
    }
}

Upvotes: 2

Views: 1026

Answers (2)

EpicPandaForce
EpicPandaForce

Reputation: 81539

@Module
public class SettingsModule {   
    @Inject SettingsManager manager;

    @Provides
    @TimeControl
    int[] provideResetTime(){
        return manager.getResetTime();
    }

    @Provides
    @ThemeControl
    int provideThemeID(){
        return manager.getTheme();
    }
}

Should be

@Module
public class SettingsModule {   
    @Provides
    @TimeControl
    int[] resetTime(SettingsManager manager) {
        return manager.getResetTime();
    }

    @Provides
    @ThemeControl
    int themeId(SettingsManager manager) {
        return manager.getTheme();
    }
}

Beware that your providers aren't scoped, so (AFAIK) a call that obtains themeId() and a call that obtains resetTime() will most likely create a new SettingsManager each time.

So you might want to put @Singleton on your provided classes.

@Singleton
public class SharedPreferencesManager {
    private SharedPreferences sharedPrefs = null;
    String prefsKey = "SHAREDPREFSKEY";
    Context applicationContext = null;

    @Inject
    SharedPreferencesManager(Context applicationContext) {
        this.applicationContext = applicationContext;
        sharedPrefs = applicationContext.getSharedPreferences(prefsKey, Context.MODE_PRIVATE); // why isn' this in a module?
    }
}

@Singleton
public class SettingsManager{
    private SharedPreferencesManager manager;
    @Inject
    SettingsManager(SharedPreferencesManager manager){
        this.manager = manager;
    }
}

Upvotes: 1

Evin1_
Evin1_

Reputation: 12866

I don't think you should have @Inject annotations in the modules since they are built to be the ones that create the dependencies and only receive other ones through the object graph or a simple constructor.


Here's an example on how you could avoid that @Inject annotation in the Module and the constructor injectors after it.

SettingsModule.java

@Module
public class SettingsModule {

    @Provides
    @TimeControl
    int[] provideResetTime(SettingsManager manager) {
        return manager.getResetTime();
    }

    @Provides
    @ThemeControl
    int provideThemeID(SettingsManager manager) {
        return manager.getTheme();
    }

    @Provides
    SettingsManager provideSettingsManager(SharedPreferencesManager sharedPreferencesManager) {
        return new SettingsManager(sharedPreferencesManager);
    }

    @Provides
    SharedPreferencesManager provideSharedPreferencesManager(@ApplicationContext Context context) {
        return new SharedPreferencesManager(context);
    }
}

SettingsManager.java

public class SettingsManager {
    private SharedPreferencesManager manager;

    SettingsManager(SharedPreferencesManager manager) {
        this.manager = manager;
    }
}

SharedPreferencesManager.java

public class SharedPreferencesManager {
    private static SharedPreferencesManager instance = null;
    private SharedPreferences sharedPrefs = null;
    String prefsKey = "SHAREDPREFSKEY";
    Context applicationContext = null;

    SharedPreferencesManager(Context applicationContext) {
        this.applicationContext = applicationContext;
        sharedPrefs = applicationContext.getSharedPreferences(prefsKey, Context.MODE_PRIVATE);
        instance = this;
    }

    public static SharedPreferencesManager getInstance() {
        return instance;
    }
}

With this, you would leave all your injection logic to your Module, and the concrete classes won't have to worry about injecting the classes themselves.

Upvotes: 0

Related Questions