Search code examples
androiddependency-injectiondagger-2dagger

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;
    }
}

Solution

  • 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.