I have a module as follows.
#Module
public class AppModule {
private final Application app;
public AppModule(Application app) {
this.app = app;
}
#Provides
#Architecture.ApplicationContext
Context provideContext() {
return app;
}
#Provides //scope is not necessary for parameters stored within the module
public Context context() {
return provideContext();
}
#Singleton
#Provides
Application provideApp() {
return app;
}
#Singleton
#Provides
SoundsRepository provideSoundsRepository(Context context, SoundsDAO soundsDAO) {
return new SoundsRepository(context, soundsDAO);
}
}
A component like this.
#Singleton
#Component(modules = AppModule.class)
public interface AppComponent {
void inject(Global global);
void inject(MainActivity mainActivity);
#Architecture.ApplicationContext
Context getContext();
Application getApplication();
void inject(PostView postView);
void inject(MediaPlayerService mediaPlayerService);
}
In activity, fragment or service, I do this
#Inject
SoundsRepository soundsRepository;
#Override
protected void onCreate(...) {
//....
((Global) getApplication()).getComponent().inject(this);
}
In SoundsRepository
#Singleton
public class SoundsRepository {
#Inject
public SoundsRepository(Context context, SoundsDAO soundsDAO) {
this.context = context;
this.soundsDAO = soundsDAO;
System.out.println(TAG + "INIT");
}
// ....
}
So, now, every time I start to access an activity or service where SoundsRepository is injected, I get a new instance, I mean, the constructor of "SoundsRepository" fires again.
What am I doing wrong?
EDIT : Inject in Application Class
public class Global extends MultiDexApplication {
protected AppComponent appComponent;
private boolean calledAlready = false;
#Override
public void onCreate() {
super.onCreate();
//if (LeakCanary.isInAnalyzerProcess(this)) return;
//LeakCanary.install(this);
initFirebasePersistance();
appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
appComponent.inject(this);
FrescoUtil.init(getApplicationContext());
}
public AppComponent getComponent() {
return appComponent;
}
}
In your module you have a method that provides an instance of SoundsRepository - good
In your AppComponent you are missing a:
SoundsRepository soundsRepository();
In your Global which extends Application/MultidexApplication you create your DaggerAppComponent - good
In your other activities/fragments/services just call:
Global application = (Global) getApplication();
SoundsRepository sr = application.getComponent().soundsRepository()
Android guarantees you have only one instance of your Application (Global) class for all other actvities/services (its somewhat like a singleton).
So keep your component in that application class, and whenever you need your class, call: (YourApplication) getApplication().getComponent().yourSingleInstanceSomething();
I created and tested sample code for you: https://github.com/zakrzak/StackDaggerTest
Dagger's #Singleton is just a scope, and does not guarantee returning a singular instance of a class.
In my understanding, if you:
void inject(PostView postView);
you tell Dagger to make everything you annotated with #Provided in AppModule accessible in your PostView as soon as you request it with:
#Inject
SoundsRepository soundsRepository;
then dagger just calls the #provided method which in your case returns a new SoundRepository instance:
#Singleton
#Provides
SoundsRepository provideSoundsRepository(Context ........) {
return new SoundsRepository(...);
}
which causes your problem
Related
i have a problem with Singleton with the Dagger2 library for Android.
My problem is im using the #Singleton but getting two different objects =[
i Have 2 Components and 2 Modules:
DispatcherComponent which includes the DispatcherModule class that provides a Dispatcher.
the Dispatcher needs instance UserStore which is provided by StoreModule.
#Singleton
#Component(modules={AppModule.class, StoreModule.class, DispatcherModule.class})
public interface DispatcherComponent {
void inject(SomeClass someClass);
}
and the DispatcherModule.class is as follows
#Module
public class DispatcherModule {
#Provides
#Singleton
public Dispatcher provideDispatcher(UserStore store) {
Log.d("DEBUG", "userStore : " + store.toString());
return new Dispatcher(store);
}
and now the StoreComponent.class
#Singleton
#Component(modules={AppModule.class, StoreModule.class})
public interface StoreComponent {
void inject(SomeOtherClass otherClass);
}
and StoreModule.class
#Module
public class StoreModule {
#Provides
#Singleton
public UserStore provideUserStore() {
return new UserStore();
}
now when im trying to inject UserStore im getting two different objects =/
public class SomeOtherClass extends Acitivity {
#Inject UserStore mStore;
public void onCreate(Bundle savedInstance) {
StoreComponent comp = ((MyApp) getApplication).getStoreComponent();
comp.inject(this);
Log.d("DEBUG", "userStore2 :" + mStore.toString());
}
}
public class SomeClass {
#Inject Dispatcher mDispatcher;
public SomeClass (Application application) {
((MyApp) application).getDispatcherComponent().inject(this);
}
and last, this is how i create the components:
public class MyApp extends Application {
public void onCreate() {
StoreModule store = new StoreModule();
StoreComponent storeComponent = DaggerStoreComponent.builder().appModule(new AppModule(this)).storeModule(storeModule).build();
DispatcherComponent disComp = DaggerDispatcherComponent.builder().appModule(new AppModule(this)).storeModule(storeModule).dispatcherModule(new DispatcherModule()).build();
}
now, when im running the Application, i get 2 different objects ! can someone help me ? how should i fix it? i dont want to have a god component..
THanks!
Note that #Singleton dost not make the object to be singleton actually, instead, it just use the DoubleCheck class to cache which is hold by the component impl generated by dagger. Check DaggerDispatcherComponent for more detail.
For this case, you can change StoreModule like below:
#Module
public class StoreModule {
private UserStore mStore;
#Provides
#Singleton
public UserStore provideUserStore() {
return getStore();
}
private UserStore getStore() {
//lazy initialized and make sure thread safe
if (null == mStore) {
#synchronized(StoreModule.class) {
if (null == mStore) {
mStore = new UserStore();
}
}
}
return mStore;
}
}
This question already has an answer here:
How do I fix Dagger 2 error '... cannot be provided [...]'?
(1 answer)
Closed 5 years ago.
I am using MVP Architecture along with Dagger2. I want to get Context in my Presenter using constructor injection.
My Activity :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_appintroduction);
activityComponent().inject(this);
ButterKnife.bind(this);
skipall.bringToFront();
}
My Presenter:
#ConfigPersistent
public class AppIntroPresenter extends BasePresenter<AppIntroMvpView> {
private final DataManager mDataManager;
private final Context ctx1;
#Inject
public AppIntroPresenter(DataManager dataManager,Context context) {
mDataManager = dataManager;
ctx1=context;
}
public void openCamView(Context ctx) {
ctx.startActivity(new Intent(ctx, CameraActivity.class));
}
}
My Application Component :
#Singleton
#Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
void inject(SyncService syncService);
#ApplicationContext Context context();
Application application();
RibotsService ribotsService();
PreferencesHelper preferencesHelper();
DatabaseHelper databaseHelper();
DataManager dataManager();
RxEventBus eventBus();
ClassService classService();
AnsweredQuestionService answeredQuestionService();
}
My Activity Component:
#PerActivity
#Subcomponent(modules = ActivityModule.class)
public interface ActivityComponent {
void inject(MainActivity mainActivity);
void inject(CameraActivity cameraActivity);
void inject(AppIntroActivity appIntroActivity);
}
My Application Module:
#Module
public class ApplicationModule {
protected final Application mApplication;
public ApplicationModule(Application application) {
mApplication = application;
}
#Provides
Application provideApplication() {
return mApplication;
}
#Provides
#ApplicationContext
Context provideContext() {
return mApplication;
}
#Provides
#Singleton
RibotsService provideRibotsService() {
return RibotsService.Creator.newRibotsService();
}
#Provides
#Singleton
ClassService provideClassService() {
return ClassService.Creator.newClassService();
}
#Provides
#Singleton
AnsweredQuestionService provideAnsweredQuestionService() {
return AnsweredQuestionService.Creator.newAnsweredQuestionService();
}
}
My Activity Module:
#Module
public class ActivityModule {
private Activity mActivity;
public ActivityModule(Activity activity) {
mActivity = activity;
}
#Provides
Activity provideActivity() {
return mActivity;
}
}
After running the code, i get error:
Error:(17, 8) error: [uk.co.ribot.androidboilerplate.injection.component.ActivityComponent.inject(uk.co.ribot.androidboilerplate.ui.AppIntro.AppIntroActivity)] android.content.Context cannot be provided without an #Provides-annotated method.
android.content.Context is injected at
uk.co.ribot.androidboilerplate.ui.AppIntro.AppIntroPresenter.<init>(…, context)
uk.co.ribot.androidboilerplate.ui.AppIntro.AppIntroPresenter is injected at
uk.co.ribot.androidboilerplate.ui.AppIntro.AppIntroActivity.mAppIntroPresenter
uk.co.ribot.androidboilerplate.ui.AppIntro.AppIntroActivity is injected at
uk.co.ribot.androidboilerplate.injection.component.ActivityComponent.inject(appIntroActivity)
I know when i add context to constructor injection in My Presenter i get this error, but i am not able to figure out why? If i remove the context from constructor :
#Inject
public AppIntroPresenter(DataManager dataManager,Context context) {
mDataManager = dataManager;
ctx1=context;
}
Then everything works fine. But i need the context from injection. Please help me with this?
Since your constructor has members, you need to create a module and create there an instance annotated with #Provides and #Singleton, same as ApplicationModule.
I'm implimenting Dagger 2 in my Android app. I have it setup in the following way:
AppComponent.java
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
AndroidSupportInjectionModule.class,
ActivityBuilder.class,
AppModule.class,
DataBaseDaoModule.class
})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application aApplication);
AppComponent build();
}
Application application();
void inject(MyApplication aApplication);
}
AppInjector.java
ublic class AppInjector {
public static void init(MyApplication aApplication) {
//Initialize dagger and inject the aApplication
DaggerAppComponent.builder().application(aApplication).build().inject(aApplication);
aApplication.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
#Override
public void onActivityCreated(Activity aActivity, Bundle aBundle) {
handleActivity(aActivity);
}
#Override
public void onActivityStarted(Activity aActivity) {
}
#Override
public void onActivityResumed(Activity aActivity) {
}
#Override
public void onActivityPaused(Activity aActivity) {
}
#Override
public void onActivityStopped(Activity aActivity) {
}
#Override
public void onActivitySaveInstanceState(Activity aActivity, Bundle aBundle) {
}
#Override
public void onActivityDestroyed(Activity aActivity) {
}
});
}
private static void handleActivity(Activity aActivity) {
if (aActivity instanceof HasActivityInjector) {
AndroidInjection.inject(aActivity);
Timber.d("injected Activity");
}
if (aActivity instanceof FragmentActivity) {
((FragmentActivity) aActivity).getSupportFragmentManager()
.registerFragmentLifecycleCallbacks(
new FragmentManager.FragmentLifecycleCallbacks() {
#Override
public void onFragmentCreated(FragmentManager fm, Fragment f,
Bundle savedInstanceState) {
if (f instanceof Injectable) {
Timber.d("injected Fragment");
AndroidSupportInjection.inject(f);
}
}
}, true);
}
}
}
AppModule.java
Module(includes = ViewModelModule.class)
class AppModule {
#Singleton
#Provides
ApiService providesApiService(OkHttpClient aClient, MyInterceptor aInterceptor) {
//Build a Retrofit object here
}
#Singleton
#Provides
OkHttpClient providesOkHTTPClient(MyInterceptor aInterceptor) {
//Setup OKHTTP here
}
}
And finally in MyApplication.Java in the onCreate method I just call the AppInjector like so: AppInjector.init(this);
All of this works and anything I put in my AppComponent's moduels, I can inject into Activities, Fragments and ViewModels.
However, I have cases where I would need a utility class, that depends on Application, for contex - and I use the utility class in various places. Or I will have a Manager class, that depends on Application, or needs something from AppModule. However, since I use these classes outside of Activities, Fragments and ViewModels I cannot just inject. How would I provide my utility classes with their dependencies and any other type of class - like a manager class?
My first thought was to create a UtilityComponent and a ManagerCompoent of sorts, however I have no idea how I would get them to work with anything in AppModuel or through my AppComponent.
Please don't just use component.inject(myObject) for everything. Always prefer constructor injection or provide it from a module where you can do additional setup steps. .inject(myObject) is intended for Framework components where you don't have access to the constructor.
My first thought was to create a UtilityComponent and a ManagerCompoent of sorts, however I have no idea how I would get them to work with anything in AppModuel or through my AppComponent.
You don't need a separate component for that. See below.
However, since I use these classes outside of Activities, Fragments and ViewModels I cannot just inject.
That has nothing to do with injection. You're talking about scopes, and it sound like your utilities are a #Singleton. Your AppComponent is a #Singleton scoped component, hence it can be used to provide your utils, too.
However, I have cases where I would need a utility class, that depends on Application, for context
If they are part of the #Singleton component, which has access to your Application, they can also be provided anywhere else. No need for more components or anything. Just declare your dependencies and don't overthink it.
Just declare your util, annotate it with #Singleton and mark the constructor with #Inject for constructor injection. #Singleton ensures that it will be provided by your AppComponent and can access the Application on which it depends.
#Singleton public class MyUtil {
private Application application;
#Inject public MyUtil(Application application) {
this.application = application;
}
}
And then you can just inject it in your Activities, Fragments, or even into other Utilities....
#Singleton public class MyUtilWrapper {
private MyUtil myUtil;
#Inject public MyUtilWrapper(MyUtil myUtil) {
this.myUtil = myUtil;
}
}
And you can inject either or both into your activity or fragment...
#Inject MyUtil myUtil;
#Inject MyUtilWrapper myUtilWrapper;
void onCreate(..) {
AndroidInjection.inject(this);
}
You do not need any modules, provides methods, or components to provide simple classes. Just make sure to add the right scope!
The dependencies provided via the application component of the app do not have a single instance.
I have the following code:
#Component (modules = AppModule.class)
public interface AppComponent {
SharedPreferences getSharedPreferences();
}
#Module
public class AppModule {
private Context appContext;
private String prefFile;
public AppModule(#NonNull Context appContext, #NonNull String prefFile) {
this.appContext = appContext;
this.prefFile = prefFile;
}
#Provides
public SharedPreferences providePreferences(){
return new AppSharedPreferences(appContext, prefFile);
}
}
#Singleton
public class AppSharedPreferences implements SharedPreferences{
public AppSharedPreferences(Context appContext, String prefFile)
//Some code
}
public class AppApplication extends Application {
private AppComponent appComponent;
private InteractorsComponent interactorsComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.builder().appModule(new AppModule(getApplicationContext(), "PREF_STORE_NAME")).build();
interactorsComponent = DaggerInteractorsComponent.builder().appComponent(appComponent).build();
}
public AppComponent getAppComponent() {
return appComponent;
}
public InteractorsComponent getInteractorsComponent() {
return interactorsComponent;
}
}
#Module
abstract class InteractorsModule {
#Singleton
#Binds
abstract InteractorA provideInteractorA(AppInteractorA interactor);
#Singleton
#Binds
abstract InteractorB provideInteractorB(AppInteractorB interactor);
}
#Singleton
#Component (modules = InteractorsModule.class, dependencies = AppComponent.class)
public interface InteractorsComponent {
InteractorA getInteractorA();
InteractorB getInteractorB();
}
#Singleton
class AppInteractorA implements InteractorA {
private AppSharedPreferences pref;
#Inject
public AppInteractor(#NonNull AppSharedPreferences pref) {
this.pref = pref;
}
//Other overridden methods
}
#Singleton
class AppInteractorB implements InteractorB {
private AppSharedPreferences pref;
#Inject
public AppInteractor(#NonNull AppSharedPreferences pref) {
this.pref = pref;
}
//Other overridden methods
}
Now, InteractorA and InteractorB to be injected in someother classes.
InteractorA and InteractorB instances themselves are singleton where ever they are injected. But of object of InteractorA and object of InteractorB, a different instance of SharedPreferences class is provided. So InteractorA has a different instance of SharedPreferences and InteractorB has a different instance of SharedPreferences.
Could someone help to make sure that both InteractorA and InteractorB have the same instance of SharedPreferences.
Thanks.
#Module
public class AppModule {
// ...
#Provides
public SharedPreferences providePreferences(){
return new AppSharedPreferences(appContext, prefFile);
}
}
Though AppSharedPreferences is annotated with #Singleton, you're calling its constructor yourself, which means that Dagger can't guarantee that there's only one SharedPreferences instance in your application. This means that you'll get a new AppSharedPreferences instance whenver you inject SharedPreferences, including through the InteractorsComponent component dependency.
You should mark providePreferences with #Singleton, or use an #Inject constructor on AppSharedPreferences so that Dagger can manage its #Singleton behavior.
after search on web for learning about this feature most topics or post was using dependency injection for Retrofit or other android useful libraries, but i have some custom class which i want to use that with DI and i can't done it, for example i have simple custom class for using SharePreference and i'm using with that as an Singleton class
in my code i can't assign correct Dagger to component on SpApplication class to use that on activities or fragments
public class SP {
private SharedPreferences preferences;
private Context context;
public SP(Context context) {
this.context = context;
}
private SharedPreferences getPrefs() {
return preferences = PreferenceManager.getDefaultSharedPreferences(context);
}
public String getString(SharedPrefsTypes propertyName) {
return getPrefs().getString(propertyName.toString(), "");
}
public int getInt(SharedPrefsTypes propertyName) {
return getPrefs().getInt(propertyName.toString(), 0);
}
...
public enum SharedPrefsTypes {
Login
}
}
now i'm trying to use DI for that:
AppModules class:
#Module
public class AppModules {
private Context context;
public AppModules(Context context) {
this.context = context;
}
#Provides
#Singleton
SP provideSharePreferences() {
SP sharePreference = new SP(context);
return sharePreference;
}
}
ApplicationComponent class:
#Component(modules = AppModules.class)
public interface ApplicationComponent {
void inject(ActivityMain activity);
}
SpApplication class:
public class SpApplication extends Application {
private static SpApplication self;
private ApplicationComponent component;
#Override
public void onCreate() {
super.onCreate();
self = this;
component = DaggerApplicationComponent.builder().build();
}
public static SpApplication get(Context context) {
return (SpApplication) context.getApplicationContext();
}
public static SpApplication getInstance() {
return self;
}
public ApplicationComponent getComponent() {
return component;
}
}
and my ActivityMain class:
public class ActivityMain extends AppCompatActivity {
#Inject
SP sharePreference;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((SpApplication) getApplication()).getComponent().inject(this);
sharePreference.setInt(SP.SharedPrefsTypes.Login, 0);
}
}
I get this error:
android.app.Application cannot be cast to com.pishguy.yendir.SpApplication
Thanks in advance
I guess you are trying to inject into ActivityMain, but since you did not provide its source, let me show you how you could inject into SpApplication. Then just copy the relevant parts into your Activity.
Few things I think you need to change.
Module:
Your AppModules class is generally OK, but I just suggest you to change the way you make use of Context - don't use it as a field, but inject it as any other service. It looks like this:
#Module
public class AppModules {
private Context context;
public AppModules(Context context) {
this.context = context;
}
#Provides // this can be non-scoped because anyway the same instance is always returned
Context provideContext() {
return this.context;
}
#Provides
#Singleton
SP provideSharePreferences(Context context) {
return new SP(context); // use method-local Context
}
}
Component:
If component injects scoped services (#Singleton is a scope), the component itself must be scoped
If you want to inject into SpApplication class, then declare it as an injection client in the component
Taking into account these two points, ApplicationComponent should look like this:
#Singleton // injects #Singleton scoped services
#Component(modules = AppModules.class)
public interface ApplicationComponent {
void inject(SpApplication application); // SpApplication is DI client (injection target)
}
Client:
I'd change few things in your SpApplication class:
The way you instantiate ApplicationComponent is incorrect
This is not a bug, but you don't really need get(Context) and getInstance() methods
In addition, since I'm showing how you inject into SpApplication, I will add the injection logic as well (which you should copy to your actual clients).
So, SpApplication (which is DI client) should look similar to this:
public class SpApplication extends Application {
#Inject SP sp; // the dependency that should be injected
private ApplicationComponent component;
#Override
public void onCreate() {
super.onCreate();
getComponent().inject(this); // this is when the actual injection takes place
}
public ApplicationComponent getComponent() {
if (component == null) {
// this is the way Dagger components should be instantiated
component = DaggerApplicationComponent.builder()
.appModules(new AppModules(this))
.build();
}
return component;
}
}
If you perform the above changes, I tend to believe that you'll be all right.
BTW, I recently completed a blog post about Dagger 2 scopes. You might want to check it if you are going to be serious about dependency injection.