Steven
Steven

Reputation: 95

Dagger 2 - Constructor Injection - Non-Activity

Just started learning Dagger 2 in order to solve a specific problem: Im trying to follow an MVVM architecture and my application has a repository class that pulls and saves settings in a CacheData class that basically wraps SharedPreferences. However, SharedPreferences has a context dependency. Since I went through all the effort of decoupling my repository and data layer from the View and Application class, passing a context seems to be a step backwards.

Here is the Repository class

public class ImgurRepository {
    private ImgurDatabase imgurDatabase;
    private ImgurGalleryDao galleryDao;

    private CompositeDisposable disposables;
    private LiveData<List<ImgurGallery>> imgurGalleries;



    public ImgurRepository(){
        this.imgurDatabase = ImgurDatabase.getInstance(MyRxApplication.getAppContext());
        this.galleryDao = imgurDatabase.getGalleryDao();
        disposables = new CompositeDisposable();
    }

    public String getCachedSearchTerm() {
        CachedData cachedData = new CachedData();
        return cachedData.getCachedSearchTerm();
    }

    public String getCachedSearchWindow(){
        CachedData cachedData = new CachedData();
        return cachedData.getCachedSearchWindow();
    }
    public String getCachedSearchType(){
        CachedData cachedData = new CachedData();
        return cachedData.getCachedSearchType();
    }

    public void setCachedSearchParams(@NonNull final String term,
                                    @NonNull final String type,
                                    @NonNull final String window) {
        CachedData cachedData = new CachedData();
        cachedData.setCachedSearchParams(term, type, window);
    }

    public LiveData<List<ImgurGallery>> getCachedGalleries() {
        return this.imgurGalleries;
    }

    public LiveData<List<ImgurGallery>> fetchGalleries(@NonNull final String searchType,
                                                              @NonNull final String searchWindow,
                                                              @NonNull final String searchTerm,
                                                              final int resultsPage){
        requestGalleries(searchType, searchWindow, searchTerm, resultsPage);
        return galleryDao.getAll();
    }

    private void requestGalleries(@NonNull final String searchType,
                               @NonNull final String searchWindow,
                               @NonNull final String searchTerm,
                               final int resultsPage) {
        Timber.d("Running fetchGalleries with arguments:\nsort='%s' \nwindow='%s'\nsearch='%s'\npage='%s'",
                searchType,
                searchWindow,
                searchTerm,
                resultsPage);
        ServiceGenerator.changeApiBaseUrl(IMGUR_API_BASE_URL);
        ImgurService service = ServiceGenerator.createService(ImgurService.class);
        Timber.d("finishing fetchGalleries request.");
        disposables.add(service.getSearchGallery(searchType,searchWindow,resultsPage,searchTerm)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Response<ImgurGalleryList>>() {
                    @Override
                    public void accept(@NonNull final Response<ImgurGalleryList> response) throws Exception {
                        Timber.d("Consumer is subscribed to imgurGalleryObservable.");
                        Timber.d(response.body().toString());
                        List<ImgurGallery> galleries = response.body().getData();
                        galleryDao.insertAll(galleries);
                    }

                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        Timber.e(throwable);
                    }
                }));
    }

    public void clearGalleries() {
        galleryDao.deleteAll();
    }

}

Here is the fixed CachedData class per Samuel's comments:

public class CachedData {
    private final String CACHED_SEARCH_TERM_KEY = "cached_search_term";
    private final String CACHED_SEARCH_WINDOW_KEY = "cached_search_window";
    private final String CACHED_SEARCH_TYPE_KEY = "cached_search_type";

    @Inject public SharedPreferences sharedPrefs;
    @Inject public Context context;

    public CachedData() {}

    public String getCachedSearchTerm() {
        return sharedPrefs.getString(CACHED_SEARCH_TERM_KEY, "");
    }
    public String getCachedSearchWindow(){
        return sharedPrefs.getString(CACHED_SEARCH_WINDOW_KEY, "");
    }
    public String getCachedSearchType(){
        return sharedPrefs.getString(CACHED_SEARCH_TYPE_KEY, "");
    }

    public void setCachedSearchParams(@Nullable final String term,
                                        @Nullable final String window,
                                        @Nullable final String type) {
        SharedPreferences.Editor editor = sharedPrefs.edit();
        if (term != null) editor.putString((CACHED_SEARCH_TERM_KEY), term);
        if (window  != null) editor.putString(CACHED_SEARCH_WINDOW_KEY, window);
        if (type != null) editor.putString(CACHED_SEARCH_TYPE_KEY, type);
        editor.apply();

    }

}

fixed AppComponent class:

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

    void inject(MyRxApplication app);
    void inject(CachedData cachedData);
    void inject(BaseActivity activity);
}

AppModule class:

@Module
public class AppModule {
    private final String SHARED_PREFERENCE_KEY = "PREFERENCE_FILE_KEY";
    private final MyRxApplication application;

    public AppModule(MyRxApplication application) {
        this.application = application;
    }

    @Provides
    @Singleton
    public Context provideApplicationContext(){
        return application;
    }

    @Provides
    @Singleton
    public SharedPreferences provideSharedPreferences() {
        return application
                .getSharedPreferences(
                        SHARED_PREFERENCE_KEY,
                        Context.MODE_PRIVATE);
    }
}

Application class:

public class MyRxApplication extends Application {
    private static Context context;
    private AppComponent appComponent;

    public MyRxApplication() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        MyRxApplication.context = getApplicationContext();
        if (LeakCanary.isInAnalyzerProcess(this)) {
            // This process is dedicated to LeakCanary for heap analysis.
            // You should not init your app in this process.
            return;
        }
        LeakCanary.install(this);

        Stetho.initializeWithDefaults(this);

        if (BuildConfig.DEBUG) {
            Timber.plant(new Timber.DebugTree());
        }

        appComponent = buildComponent();
        appComponent.inject(this);
        appComponent.cachedData();

    }

    public AppComponent buildComponent(){
        return DaggerAppComponent
                .builder()
                .appModule(new AppModule(this))
                .build();
    }

    public AppComponent getAppComponent() {
        return appComponent;
    }

    public static Context getAppContext() {
        return MyRxApplication.context;
    }

}

Is it possible to use Dagger to inject the SharedPreferences object into the CachedData constructor? I am constructing many CachedData objects because I originally did not want to use a Singleton, but would it make sense to just inject the CachedData object as a singleton instead? I'm really unsure of the correct way to use Dagger now and get this to work because every parameter I set in the CacheData constructor needs to be supplied when I create it, even though I thought I am using Dagger to do that...?

Upvotes: 1

Views: 296

Answers (1)

Samuel Eminet
Samuel Eminet

Reputation: 4737

Hum so from you additionnal commment as i understand it, you don't want to use injection for your class CachedData and so want to instantiate it manually. If this is the case you may remove you injected constructor arguments and inject your variables directly like this :

@Inject
SharedPreferences sharedPrefs;
@Inject
Context context;

@Inject
public CachedData() {}

Upvotes: 1

Related Questions