0
votes

I'm trying to refactor my app using MVP pattern and Dagger 2 for Dependency Injection.

I Create module that provides Application Context and I want get Context to get SharedPreferences on Model Layer.

I inject Context on Presenter layer and it's working with SharedPreference, but when I move to Model layer, Dagger inject a null value on Context variable.

Inject

@Inject
public Context mContext;

App Module

AppModule provides Application Context

@Module
public class AppModule {
    private App app;

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

    @Provides
    public Context providesApp(){
        return app.getApplicationContext();
    }
}

Application

public class App extends Application {

    private AppComponent appComponent;

    @Override
    public void onCreate() {
        super.onCreate();

        appComponent = DaggerAppComponent.builder()
                .appModule(new AppModule(this))
                .mainModule(new MainModule())
                .build();
    }

    public AppComponent getAppComponent(){
        return appComponent;
    }
}

App Component

@Component(modules = {AppModule.class,MainModule.class})
public interface AppComponent {
    void inject(MainActivity activity);
    void inject(LoginActivity activity);
}

Main Module

@Module
public class MainModule {

    @Provides
    public MainContract.Model providesMainModel(){
        return new MainModel();
    }

    @Provides
    public LoginContract.Model providesLoginModel(){
        return new LoginModel();
    }
}
1
You can take a look at this sample project github.com/mmirhoseini/marvel and this article medium.com/@m_mirhoseini/… to get more familiar with MVP. - Mohsen Mirhoseini
@MohsenMirhoseiniArgi I've read those article and GitHub repo before I ask on SO. Thanks for your great article and sample :D - Sucipto

1 Answers

2
votes

Since you have the Context in your App Module, you can add the Context as parameter to LoginModel constructor.

@Provides
public LoginContract.Model providesLoginModel(Context contect){
   return new LoginModel(contect);
}

@Provides
public LoginContract.Presenter providesLoginPresenter(LoginContract.Model model){
   return new LoginPresenter(model);
}

But bear in mind that you must use android packages only on your Activities and Fragments for easy testing. So your approach is wrong.

EDIT

Well for me, the best option is to create a class for SharedPreferences like this

public class Preferences {
   private final SharedPreferences preferences;
   private String key;
   private String defaultValue;

   public StringPreferences(@NonNull SharedPreferences preferences) {
     this.preferences = preferences;
   }

   @Nullable
   public String get(String mykey) {
     return preferences.getString(mykey, defaultValue);
   }

   public void set(@NonNull String mykey, @Nullable String value) {
     preferences.edit().putString(mykey, value).apply();
   }

   // same way for integers, longs, doubles and booleans

   public boolean isSet() {
     return preferences.contains(key);
   }

   public void delete() {
    preferences.edit().remove(key).apply();
   }
}

Then in App Module I Provide the SharedPreferences

@Provides
@Singleton
public SharedPreferences provideSharedPreferences(Context context) {
    return PreferenceManager.getDefaultSharedPreferences(context);
}

@Provides
@Singleton
public Preferences providePreferences(SharedPreferences prefs) {
    return new Preferences(prefs);
}

Finally in Main Module

@Provides
public LoginContract.Model providesLoginModel(Preferences prefs){
   return new LoginModel(prefs);
}

@Provides
public LoginContract.Presenter providesLoginPresenter(LoginContract.Model model){
   return new LoginPresenter(model);
}