I was following some examples to work on dagger2. Here I was using dependencies on HomeFragmentComponent to provide reference of context from another scope but its not working.
ContextModule
#Module
public class ContextModule {
private final Context context;
public ContextModule(Context context) {
this.context = context;
}
#Provides
#ShikshyaScope
public Context context(){
return context;
}
}
Network Module :
#Module(includes = ContextModule.class)
public class NetworkModule {
#Provides
#ShikshyaScope
public File file(Context context){
File cacheFile = new File(context.getCacheDir(),"okhttp_cache");
cacheFile.mkdirs();
return cacheFile;
}
ShikshyaApplicationComponent:
#ShikshyaScope
#Component(modules = {NetworkModule.class, PicassoModule.class, StorageModule.class})
public interface ShikshyaApplicationComponent {
void injectShikshyaApplication(ShikshyaBusApplication shikshyaBusApplication);
}
Home Fragment Module :
#Module
public class HomeFragmentModule {
public final HomeFragment homeFragment;
public HomeFragmentModule(HomeFragment homeFragment) {
this.homeFragment = homeFragment;
}
#Provides
#HomeFragmentScope
public HomeFragment homeFragment(){
return homeFragment;
}
#Provides
#HomeFragmentScope
public HomeFragmentView homeFragmentView(HomeFragment homeFragment){
return (HomeFragmentView)homeFragment;
}
#Provides
#HomeFragmentScope
public HomeFragmentPresenter homeFragmentPresenter(HomeFragmentView homeFragmentView,MetaDatabaseRepo metaDatabaseRepo){
return new HomeFragmentPresenter(homeFragmentView,metaDatabaseRepo);
}
#Provides
#HomeFragmentScope
public DatabaseHelper databaseHelper(Context context){
return OpenHelperManager.getHelper(context,DatabaseHelper.class);
}
}
HomeFragmentComponent :
#HomeFragmentScope
#Component(modules = HomeFragmentModule.class,dependencies =ShikshyaApplicationComponent.class)
public interface HomeFragmentComponent {
void injectHomeFragment(HomeFragment homeFragment);
}
Now I get error as
error: android.content.Context cannot be provided without an #Provides-annotated method.
android.content.Context is injected at com.bihani.shikshyabus.di.module.HomeFragmentModule.databaseHelper(context)
com.bihani.shikshyabus.database.DatabaseHelper is injected at
You should include ContextModule as HomeFragmentModule dependency so Dagger2 be able to provide context to DatabaseHelper
#Module(includes = ContextModule.class)
public class HomeFragmentModule {
// Your stuff here
}
Related
#Singleton
public class AppPreferenceHelper implements PreferenceHelper {
static final String PREFS_APP_STATE = "prefsAppState";
static final String APP_STATE_LOGIN = "logIn";
String PREF_NAME ="appPreference" ;
private SharedPreferences sharedPreferences;
#Inject
AppPreferenceHelper(#ApplicationContext Context context) {
this.sharedPreferences = context.getSharedPreferences(PreferenceConstant.PREF_NAME,Context.MODE_PRIVATE);
}
#Override
public void setAppState(String state) {
sharedPreferences.edit().putString(PREFS_APP_STATE,state);
}
#Override
public String getAppState() {
return sharedPreferences.getString(PREFS_APP_STATE,APP_STATE_LOGIN);
}
}
#PerActivity
#Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {
void inject(SplashActivity mainActivity);
void inject(LogInActivity logInActivity);
}
#Module
public class ApplicationModule {
private final Application mApplication;
public ApplicationModule(Application application) {
mApplication = application;
}
#Provides
#ApplicationContext
Context provideContext() {
return mApplication;
}
#Provides
Application provideApplication() {
return mApplication;
}
#Provides
#Singleton
AppPreferenceHelper providePreferencesHelper(AppPreferenceHelper appPreferencesHelper) {
return appPreferencesHelper;
}
}
#Module
public class ActivityModule {
private Activity mActivity;
public ActivityModule(Activity activity) {
mActivity = activity;
}
#Provides
#ActivityContext
Context provideContext() {
return mActivity;
}
#Provides
Activity provideActivity`enter code here`() {
return mActivity;
}`enter code here`
#Provides
#PerActivity
SpashMvpPresenter<SplashView> provideSplashPresenter(
SplashPresenter<SplashView> presenter) {
return presenter;
}
}
Error:(20, 10) error: com.d2u.android.data.preference.PreferenceHelper
cannot be provided without an #Provides-annotated method.
com.d2u.android.data.preference.PreferenceHelper is injected at
com.d2u.android.ui.splash.SplashPresenter.(preferenceHelper)
com.d2u.android.ui.splash.SplashPresenter
is injected at
com.d2u.android.di.module.ActivityModule.provideSplashPresenter(presenter)
com.d2u.android.ui.splash.SpashMvpPresenter
is injected at com.d2u.android.ui.splash.SplashActivity.mPresenter
com.d2u.android.ui.splash.SplashActivity is injected at
com.d2u.android.di.component.ActivityComponent.inject(mainActivity)
probably because you are injecting a PreferenceHelper, but your #Provides annotated method returns AppPreferenceHelper instead of PreferenceHelper
#Provides
PreferenceHelper providePreferencesHelper(AppPreferenceHelper appPreferencesHelper) {
return appPreferencesHelper;
}
Differently you could use #Binds
#Binds
abstract PreferenceHelper providePreferencesHelper(AppPreferenceHelper appPreferencesHelper);
for that you are gonna need a different abstract #Module, tho
I am confused about Dagger2 in Android.
I use two scope. #Singleton, #PerActivity
This is my Code. I simplyfy my code.
//ApplicationComponent.java
#Singleton
#Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {
#Named("packageName") String packageName();
}
//ApplicationModule.java
#Module
public class ApplicationModule {
#Provides
#Singleton
public Context provideApplicationContext() {
return MyApplication.getContext();
}
#Provides
#Singleton
#Named("packageName")
public String providePackageName(Context context) {
return context.getPackageName();
}
}
//UserComponent.java
#PerActivity
#Component(modules = {UserModule.class})
public interface UserComponent {
void inject(MainActivity activity);
}
//UserModule.java
#Module
public class UserModule {
String packageName;
public UserModule(String packageName) {
this.packageName = packageName;
}
#Provides
#PerActivity
UserRepositoryImpl provideUserRepositoryImpl() {
return new UserRepositoryImpl(packageName);
}
}
for inject appVersion, packagename in UserModule
DaggerChatComponent.builder()
.userModule(new UserModule(getApplicationComponent().packageName()))
.build();
but it looks not great. how can i inject when use different Scope??
your ApplicationModule.java is correct
#Module
public class ApplicationModule {
private Application application;
public ApplicationModule(Application application){
this.application = application;
}
#Provides
#Singleton
Context provideContext(){
return application;
}
#Provides
#Singleton
#Named("packagename")
public String providePackageName(Context context) {
return context.getPackageName();
}
}
and it's component class is also right ApplicationComponent.java
#Singleton
#Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {
#Named("packagename") String providepackagename();
}
but in the UserModule.java you need not pass the package name object , dagger's object graph does this for you.
#Module
public class UserModule {
public UserModule() {
}
#Provides
#PerActivity
UserRepositoryImpl provideUserRepositoryImpl(#Named("packagename") String packageName) {
return new UserRepositoryImpl(packageName);
}
}
and the next step is while writing the component class for this module add the application component as a dependency ie, your UserComponent.java looks like this
#PerActivity
#Component(dependencies = {ApplicationComponent.class},modules = {UserModule.class})
public interface UserComponent {
void inject(MainActivity mainActivity);
}
with the activity scope as PerActivity.lava
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface PerActivity {
}
with this example UserRepositoryImpl.java as
class UserRepositoryImpl {
private String packagename;
public UserRepositoryImpl(String packagename){
this.packagename = packagename;
}
String getPackagename(){
return packagename;
}
}
you can finally inject this in your activity.(MainActivity.java)
public class MainActivity extends AppCompatActivity {
#Inject
UserRepositoryImpl userRepository;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ApplicationComponent component=DaggerApplicationComponent.builder().applicationModule(new ApplicationModule(getApplication())).build();
UserComponent userComponent=DaggerUserComponent.builder().applicationComponent(component).userModule(new UserModule()).build();
userComponent.inject(this);
Log.e("name"," "+userRepository.getPackagename());
}
}
I would like to implement Repository module to handle data operations. I have JSON file in row directory and want create concrete Repository implementation to get data from file. I'm not sure if I can use Context as attribute in the constructor or method of Repository.
e.g.
public class UserRepository {
UserRepository() {}
public List<User> loadUserFromFile(Context contex) {
return parseResource(context, R.raw.users);
}
}
IMHO, you should use DI (Dependency Injection) like Dagger2, to provide you Context something like,
AppModule.class
#Module
public class AppModule {
private Context context;
public AppModule(#NonNull Context context) {
this.context = context;
}
#Singleton
#Provides
#NonNull
public Context provideContext(){
return context;
}
}
MyApplication.class
public class MyApplication extends Application {
private static AppComponent appComponent;
public static AppComponent getAppComponent() {
return appComponent;
}
#Override
public void onCreate() {
super.onCreate();
appComponent = buildComponent();
}
public AppComponent buildComponent(){
return DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build();
}
}
UserRepository.class
#Singleton
public class UserRepository {
UserRepository() {}
#Inject
public List<User> loadUserFromFile(Context contex) {
return parseResource(context, R.raw.users);
}
}
Happy Coding..!!
I don't see any harm in passing the context as an attribute. If you dislike the idea then you can retrieve the context via a convenient method : Static way to get 'Context' on Android?
I have two module, AppModule and SplashViewModule
For AppModule:
#Module
public final class AppModule {
#NonNull
private final MyApplication mApp;
public AppModule(#NonNull MyApplication app) {
mApp = app;
}
#Provides
public Context provideAppContext() {
return mApp;
}
#Provides
public MyApplication provideApp() {
return mApp;
}
#Singleton
#Provides
public UserManager provideUserManager() {
return new UserManager();
}
}
For SplashviewModule
#Module
public final class SplashViewModule {
#Inject
UserManager mUserManager;
#Provides
public SplashInteractor provideInteractor() {
return new SplashInteractorImpl(mUserManager);
}
#Provides
public PresenterFactory<SplashPresenter> providePresenterFactory(#NonNull final SplashInteractor interactor) {
return new PresenterFactory<SplashPresenter>() {
#NonNull
#Override
public SplashPresenter create() {
return new SplashPresenterImpl(interactor);
}
};
}
}
And I inject these to my activity like this:
#Override
protected void setupComponent(#NonNull AppComponent parentComponent) {
DaggerSplashViewComponent.builder()
.appComponent(parentComponent)
.splashViewModule(new SplashViewModule())
.build()
.inject(this);
}
But this does not work. The UserManager would be null. How can I get the singleton instance of UserManager created by AppModule and inject it to SplashViewModule?
You don't have to declare UserManager mUserManager; in SplashViewModule. Just add a UserManager parameter for the method provideInteractor.
#Provides
public SplashInteractor provideInteractor(UserManager userManager) {
return new SplashInteractorImpl(userManager);
}
I'm studying the Dagger 2 library and faced a problem of passing parameters which could not be obtained from "extends Application" class.
For example, I need to pass FragmentManager to ViewPager adapter constructor. How would you do that?
MyApplication:
public class MyApplication extends Application {
private MyAppComponent mMyAppComponent;
#Override
public void onCreate() {
super.onCreate();
Context context = getApplicationContext();
mMyAppComponent= DaggerMyAppComponent.builder()
.utilityModule(new UtilityModule())
.locationModule(new LocationModule(context))
.appModule(new AppModule(context))
.pagerAdapterModule(new PagerAdapterModule(context)) //this one is problematic
.build();
}
public MyAppComponent getmMyAppComponent() {
return mMyAppComponent;
}
}
MyAppComponent:
#Singleton
#Component(
modules = {
UtilityModule.class,
LocationModule.class,
PagerAdapterModule.class,
AppModule.class
}
)
public interface MyComponent {
FragmentManager fragmentManager(); //is it right?
public void inject(NavigationActivity navigationActivity);
}
AppModule:
#Module
public class AppModule {
Context context;
public AppModule(Context context) {
this.context = context;
}
#Provides
#Singleton
Context provideApplicationContext() {
return context;
}
}
PagerAdapterModule:
#Module
public class PagerAdapterModule {
Context context;
public PagerAdapterModule(Context context) {
this.context = context;
}
#Provides
#Singleton TabsPagerAdapter provideTabsPagerAdapter(FragmentManager fragmentManager) {
return new TabsPagerAdapter(context, fragmentManager);
}
}
NavigationActivity:
#Inject TabsPagerAdapter mTabsAdapter; //I want this to be initialized
You need to have a FragmentActivity to get a FragmentManager, which means you cannot accomplish it in Application level, because it's too soon. So you have to create a component dependency from AppComponent, let's name it ActivityComponent, which will see all dependencies that AppComponent provides.
#Component(modules = ActivityModule.class, dependencies = MyAppComponent.class)
public interface ActivityComponent {
void inject(MainActivity mainActivity);
}
#Module
public class ActivityModule {
FragmentActivity activity;
public ActivityModule(FragmentActivity activity) {
this.activity = activity;
}
#Provides
TabsPagerAdapter provideTabsPagerAdapter() {
return new TabsPagerAdapter(activity, activity.getSupportFragmentManager());
}
}
Now in onCreate() of your activity:
public class MainActivity extends AppCompatActivity {
#Inject
TabsPagerAdapter tabsPagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActivityComponent activityComponent = DaggerActivityComponent.builder()
.activityModule(new ActivityModule(this))
.myAppComponent(MyApplication.getMyAppComponent())
.build();
activityComponent.inject(this);
}
}
i have this constructor for viewPagerAdapter and fragment that injected
#Inject
DashboardFragment dashboardFragment;
#Inject
public HomeViewPageAdapter(FragmentManager fragmentManager) {
super(fragmentManager , BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
MyApplication.component.inject(this);
}
and create this module
#Module
public class HomeActivityModule {
#Provides
HomeViewPageAdapter provideHomeViewPageAdapter(HomeActivity homeActivity) {
return new HomeViewPageAdapter(homeActivity.getSupportFragmentManager());
}
}
and provide HomeActivity
#Module
public class HomeActivityModule {
#Provides
HomeViewPageAdapter provideHomeViewPageAdapter(HomeActivity homeActivity) {
return new HomeViewPageAdapter(homeActivity.getSupportFragmentManager());
}
}
finally in HomeActivity can inject viewPagerAdapter like this
#Inject
HomeViewPageAdapter homeViewPageAdapter;
You can pass the FragmentManager to the PagerAdapterModule:
Context context;
FragmentManager fragmentManager;
public PagerAdapterModule(Context context, FragmentManager fragmentManager) {
this.context = context;
this.fragmentManager = fragmentManager;
}