I have this AppModule class that contains a few functions:
#Singleton
#Provides
static FirebaseFirestore provideFirebaseFirestore() {
return FirebaseFirestore.getInstance();
}
#Singleton
#Provides
#Named("barsRef")
static CollectionReference provideBarsCollRef(FirebaseFirestore db) {
return db.collection("bars");
}
Now I want to inject in my repository class an instance of CollectionReference but I get the following error:
error: [Dagger/MissingBinding] com.google.firebase.firestore.CollectionReference cannot be provided without an #Inject constructor or an #Provides-annotated method.
This my repository class:
#Singleton
class BarsRepository {
private CollectionReference barsRef;
#Inject
BarsRepository(#Named("barsRef") CollectionReference barsRef) {
this.barsRef = barsRef;
}
//Method where I use barsRef
}
And this is my AppComponent class:
#Singleton
#Component(modules = {AndroidSupportInjectionModule.class, AppModule.class, ActivityBuildersModule.class, BarsViewModelModule.class})
public interface AppComponent extends AndroidInjector<BaseApplication> {
#Component.Builder
interface Builder{
#BindsInstance
Builder application(Application application);
AppComponent build();
}
}
If I remove the #Named("barsRef"), it works fine.
Here is the solution.
#Singleton
#Provides
#Named("barsRef")
static CollectionReference provideBarsCollRef(FirebaseFirestore db) {
return db.collection("bars");
}
Remove Singleton from above code. It will work.
The reason why it is happening is because of singleton Scope. Singleton is used to ensure that only one object will be shared within that component.
Related
I can't understand how properly inject Context when I have a component with multiple modules dependencies and I want use Component.Builder annotation.
I have an application module:
#Module
public class ApplicationModule {
#Provides
public AppDatabase provideDatabase(Context context){
return AppDatabase.getInstance(context);
}
}
And here is my ApplicationComponent where I used the Component.Builder in order to provide the context at the dependency graph:
#Singleton
#Component(modules = { ApplicationModule.class } )
public interface ApplicationComponent {
void inject(MainFragment mainFragment);
#Component.Builder
interface Builder {
#BindsInstance
Builder context(Context context);
ApplicationComponent build();
}
}
From my custom application I use the following code in order to provide the context:
appComponent = DaggerApplicationComponent.builder().context(getApplicationContext()).build();
Then I have another Dagger module, used for providing my ViewModelsFactory:
ViewModelModule
#Module
public class ViewModelModule {
#Singleton
#Provides
public MainFragmentViewModelFactory provideMainFragmentViewModelFactory(IVehicleProvider vehicleProvider, IPaymentProvider paymentProvider, IBackgroundOperationResponse response){
return new MainFragmentViewModelFactory(vehicleProvider, paymentProvider, response);
}
}
And the relative ViewModelComponent, where I used again the Component.Builder and as you can see I have three modules here: ViewModelModule, ApplicationModule and ProviderModule
#Singleton
#Component( modules = { ViewModelModule.class, ApplicationModule.class, ProviderModule.class })
public interface ViewModelComponent {
MainFragmentViewModelFactory getMainFragmentViewModelFactory();
#Component.Builder
interface Builder{
#BindsInstance
Builder context(Context context);
#BindsInstance
Builder response(IBackgroundOperationResponse response);
ViewModelComponent build();
}
}
Finally, since MainFragmentViewModelFactory requires IVehicleProvider and IPaymentProvider, I have another module:
ProviderModule
#Module
public abstract class ProviderModule {
#Singleton
#Binds
abstract IVehicleProvider bindVehicleProvider(VehicleProvider vehicleProvider);
#Singleton
#Binds
abstract IPaymentProvider bindPaymentProvider(PaymentProvider paymentProvider);
}
The followings are the constructors of PaymentProvider and VehicleProvider:
#Inject
public PaymentProvider(AppDatabase db){
this.db = db;
}
#Inject
public VehicleProvider(AppDatabase db){
this.db = db;
}
They require an AppDatabase which is provided on ApplicationModule class. AppDatabase requires a Context which is again provided in the ApplicationComponent using the Component.Builder
This code works properly, when I need to use mainFragmentViewModelFactory I just call
mainFragmentViewModelFactory = DaggerViewModelComponent
.builder()
.context(context)
.response(this)
.build()
.getMainFragmentViewModelFactory();
However, I'm not sure I did the correct steps since in the ViewModelModule I requested again a Context dependency, but is already provided in the ApplicationModule. Have I done the correct steps? Instead of create again a BindsInstance Context in the ViewModelComponent can I use the one privided in ApplicationModule?
I use Dagger 2 in my android project and want to use the #Inject in an Activity and inside another Activity which extends the first one -> MainActivity extends NetworkBaseActivity. In both Activities inside the onCreate() methods I have this:
AndroidInjection.inject(this);
Also, I have the next structure:
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
AppModule.class,
ActivityBuilderModule.class,
WebServiceModule.class})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
AppComponent build();
}
void inject(App app);
}
#Module
public abstract class ActivityBuilderModule {
#ContributesAndroidInjector(modules = {NetworkActivityModule.class})
public abstract NetworkBaseActivity bindNetworkBaseActivity();
#ContributesAndroidInjector(modules = {MainActivityModule.class})
public abstract MainActivity bindMainActivity();
}
#Module
public class NetworkActivityModule {
#Provides
public NetworkViewModelFactory
provideNetworkViewModelFactory(AuthRepository authRepository) {
return new NetworkViewModelFactory(authRepository);
}
#Provides
public AuthRepository provideAuthRepository(WebServiceApi webServiceApi,
SharedPreferencesManager sharedPreferencesManager) {
return new AuthRepository(webServiceApi, sharedPreferencesManager);
}
}
#Module
public class MainActivityModule {
}
And in the onCreate() callback of my Application class I have this:
DaggerAppComponent
.builder()
.application(this)
.build()
.inject(this);
But receive this error:
error: [Dagger/MissingBinding] [dagger.android.AndroidInjector.inject(T)]
com.example.android.view.base.NetworkViewModelFactory cannot be provided
without an #Inject constructor or an #Provides-annotated method.
A binding with matching key exists in component:
com.example.android.data.di.ActivityBuilderModule_BindNetworkBaseActivity
.NetworkBaseActivitySubcomponent
com.example.android.view.base.NetworkViewModelFactory is injected at
com.example.android.view.base.NetworkBaseActivity.mViewModelFactory
com.example.android.view.main.MainActivity is injected at
dagger.android.AndroidInjector.inject(T)
component path: com.example.android.data.di.AppComponent ?
com.example.android.data.di.ActivityBuilderModule_BindMainActivity
.MainActivitySubcomponent
Any suggestions? :)
As MainActivity extends from NetworkBaseActivity you just need an AndroidInjector for the MainActivity. NetworkBaseActivity will then be injected through MainActivity.
#Module
public class NetworkActivityModule {
#ContributesAndroidInjector(modules = { NetworkActivityModule.class })
public abstract MainActivity bindMainActivity();
}
You have to inject the constructor of your NetworkViewModelFactory class. The constructor should look something like this:
#Inject
public NetworkViewModelFactory(AuthRepository authRepository) { }
But I'm not sure, it would be better to see the NetworkViewModelFactory class code too.
I am writing an app where I am using Dagger2 for dependency Injection. I am new to dagger. I am getting confused on how it works and I am not able to figure it out how to use this library. I have tried writing below modules and components with the help of examples in blogs. I have mentioned the purpose of that module. Please correct me if I am wrong.
App Module and Component : It should generate a singleton instance across the application and I want to use this instance to access some android resources
#Module
public class AppModule {
private BaseApplication baseApplication;
public AppModule(BaseApplication baseApplication){
this.baseApplication = baseApplication;
}
#Provides
#Singleton
BaseApplication providesApplication(){
return baseApplication;
}
}
#Singleton
#Component(modules = {AppModule.class})
public interface AppComponent {
BaseApplication getBaseApplication();
}
Instantiation of AppComponent
public class BaseApplication extends Application {
private AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.builder().appModule(new
AppModule(this)).build();
}
public AppComponent getAppComponent(){
return appComponent;
}
}
The above code works fine. I am getting problems in the below code
Location Module and Component : Is it Ok if I don't have a constructor
for the below module?
#Module
public class LocationModule {
#IntDef(
{LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY,
LocationRequest.PRIORITY_HIGH_ACCURACY,
LocationRequest.PRIORITY_LOW_POWER,
LocationRequest.PRIORITY_NO_POWER})
#Retention(RetentionPolicy.SOURCE)
public #interface LocationPriority {}
#Provides
#Singleton
#Named("default")
LocationRequest providesLocationRequest(){
return new LocationRequest()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setInterval(Constants.LOCATION_UPDATE_INTERVAL_MS)
.setFastestInterval(Constants.LOCATION_UPDATE_FASTEST_INTERVAL_MS)
.setSmallestDisplacement(Constants.LOCATION_UPDATE_DISPLACEMENT_METERS);
}
#Provides
#Singleton
#Named("custom")
LocationRequest providesCustomLocationRequest(int interval, int
fastestInterval, #LocationPriority int priority, float updateDisplacement) {
return new LocationRequest()
.setPriority(priority)
.setInterval(interval)
.setFastestInterval(fastestInterval)
.setSmallestDisplacement(updateDisplacement);
}
#Provides
#Singleton
LocationSettingsRequest providesLocationSettingsRequest(LocationRequest
locationRequest){
return new LocationSettingsRequest.Builder()
.addLocationRequest(locationRequest)
.build();
}
#Provides
#Singleton
FusedLocationProviderClient providesFusedLocationClient(BaseApplication
baseApplication){
return LocationServices.getFusedLocationProviderClient(baseApplication);
}
#Provides
#Singleton
SettingsClient providesSettingsClient(BaseApplication baseApplication){
return LocationServices.getSettingsClient(baseApplication);
}
}
#Singleton
#Component(modules = {LocationModule.class, AppModule.class})
public interface LocationComponent {
**Do we Really need a method here?**
void inject(GetLocationUseCase getLocationUseCase);
}
I am getting the following error
error: cannot access Nullable class file for
javax.annotation.Nullable not found
after using #Inject in below class. If I remove #Inject the error goes away.
public final class GetLocationUseCase implements
UseCaseContract.BusinessLogic {
UseCaseContract.Mediator mediator;
#Inject
FusedLocationProviderClient fusedLocationProviderClientl;
#Override
public void onInitialize() {
BaseApplication application = mediator.requestUserContext();
DaggerLocationComponent.builder().appModule(new
AppModule(application)).locationModule(new
LocationModule()).build().inject(this);
}
Why am I getting that error? Any help would be appreciated. Thanks in advance.
You need to add missing modules in your Component ..
For example, you will get this error if you remove NetModule from below
#Component(modules = {AppModule.class, NetModule.class, MainModule.class})
public interface AppComponent {
...
In case someone like me out there. I forgot to add #Provides annotation to the Dagger Module.
In my case I had a class with #Inject annotated constructor which extended another 3rd party class which had #Nullable annotation on one of the method parameters. Once I removed this extension everything started to work normally.
After wasting about 1 day on this issue, I found out that I need to temporarily add findbugs dependency like this:
implementation 'com.google.code.findbugs:jsr305:3.0.2'
Then dagger will print the actual error.
I'm trying to use the latest version of Dagger 2 V2.11 for Android
Here is my code:
AppComponent:
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
AppModule.class,
ActivityBuildersModule.class,
FragmentBuildersModule.class
})
public interface AppComponent {
void inject(MyApplication myApplication);
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
AppComponent build();
}
#ExceptionRequestsQualifier
ExceptionRequestsServices exceptionRequestsServices();
}
AppModule:
#Module(includes = {ActivityModule.class, FragmentModule.class})
public class AppModule {
#Provides
CompositeDisposable provideCompositeDisposable() {
return new CompositeDisposable();
}
#Provides
#ExceptionRequestsQualifier
ExceptionRequestsServices provideExceptionRequests() {
return new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(APIConstants.EXCEPTION_REQUESTS_BASE_URL)
.build()
.create(ExceptionRequestsServices.class);
}
#Singleton
#Provides
NetworkManager provideNetworkManager(Application app) {
return new NetworkManager(app);
}
}
ActivityBuildersModule:
#Module
public abstract class ActivityBuildersModule {
#ActivityScope
#ContributesAndroidInjector
abstract ExceptionRequestsActivity contributeExceptionRequestsActivity();
}
ActivityModule:
#Module()
public abstract class ActivityModule {
#Provides
#ActivityScope
static ExceptionRequestsMvpPresenter<ExceptionRequestsMvpView> bindExceptionRequestsPresenter(
ExceptionRequestsPresenter<ExceptionRequestsMvpView> presenter) {
return presenter;
}
}
FragmentBuildersModule:
#Module
public abstract class FragmentBuildersModule {
#FragmentScope
#ContributesAndroidInjector
abstract AddApplicantFragment contributeAddApplicantFragment();
#FragmentScope
#ContributesAndroidInjector
abstract PledgeFragment contributePledgeFragment();
}
FragmentModule:
#Module()
public abstract class FragmentModule {
#Provides
#FragmentScope
static AddApplicantMvpPresenter<AddApplicantMvpView> bindAddApplicantPresenter(
AddApplicantPresenter<AddApplicantMvpView> presenter) {
return presenter;
}
#Provides
#FragmentScope
static PledgeMvpPresenter<PledgeMvpView> bindPledgePresenter(
PledgePresenter<PledgeMvpView> presenter) {
return presenter;
}
}
AddApplicantPresenter:
public class AddApplicantPresenter<V extends AddApplicantMvpView> extends BasePresenter<V> implements AddApplicantMvpPresenter<V> {
#Inject
#ExceptionRequestsQualifier
ExceptionRequestsServices mExceptionRequestsServices;
#Inject
NetworkManager mNetworkManager;
#Inject
public AddApplicantPresenter(CompositeDisposable compositeDisposable) {
super(compositeDisposable);
}
}
AddApplicantMvpPresenter:
#FragmentScope
public interface AddApplicantMvpPresenter<V extends AddApplicantMvpView> extends MvpPresenter<V> {
void addApplicant(String name, String qatarId,
String date, String mobile,
ChosenImage chosenImage);
}
ActivityScope:
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface ActivityScope {
}
FragmentScope:
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface FragmentScope {
}
Error Log:
Error:(21, 1) error: mypackagename.di.component.AppComponent scoped with #Singleton may not reference bindings with different scopes:
#Provides #mypackagename.di.scope.ActivityScope mypackagename.ui.exceptionrequests.ExceptionRequestsMvpPresenter<mypackagename.ui.exceptionrequests.ExceptionRequestsMvpView> mypackagename.di.module.ActivityModule.bindExceptionRequestsPresenter(mypackagename.ui.exceptionrequests.ExceptionRequestsPresenter<mypackagename.ui.exceptionrequests.ExceptionRequestsMvpView>)
#Provides #mypackagename.di.scope.FragmentScope mypackagename.ui.addapplicant.AddApplicantMvpPresenter<mypackagename.ui.addapplicant.AddApplicantMvpView> mypackagename.di.module.FragmentModule.bindAddApplicantPresenter(mypackagename.ui.addapplicant.AddApplicantPresenter<mypackagename.ui.addapplicant.AddApplicantMvpView>)
#Provides #mypackagename.di.scope.FragmentScope mypackagename.ui.pledge.PledgeMvpPresenter<mypackagename.ui.pledge.PledgeMvpView> mypackagename.di.module.FragmentModule.bindPledgePresenter(mypackagename.ui.pledge.PledgePresenter<mypackagename.ui.pledge.PledgeMvpView>)
Modules & Components can't have different Scopes
You can have Components to have multiple Scopes and this can solve it.
Try to move it to different component and add it as component dependencies
I hope in future they can solve this, the way I've done it in my project.
Currently, Dagger2 allows module with NoScope & single scope. This should match with your components.
Thumb Rule:: Different scopes have different components.
For your application, you need three components,
FragmentComponent (FragmentScope) :- (Ideally this should be ActivityComponent)
ApplicationComponent (Singleton)
https://medium.com/#patrykpoborca/making-a-best-practice-app-4-dagger-2-267ec5f6c89a
Read more about scopes.
Previously I had only one AppComponent with four modules (AppModule, NetworkModule, StorageModule, PresentersModule) and injected singletons everywhere. Recently, I decided to make small refactoring in my app and divide it into scopes. I think, presenters can live within activities only, so I created #ActivityScope and ActivityModule, but the project cannot be compiled because of my misunderstanding how to mix these scopes.
I've read a lot of articles and questions at stackoverflow, but everywhere there are simple examples where modules are independent. In my case such thing as
#Singleton
#Component(modules = { AppModule.class, StorageModule.class, NetworkModule.class })
public interface AppComponent {
ActivityComponent plus(PresentersModule module); // <-- error
}
is not working. I get this error:
Error:(19, 1) error: com.my.package.di.component.ActivityComponent scoped with #com.my.package.di.scope.ActivityScope may not reference bindings with different scopes:
#Provides #Singleton android.app.Application com.my.package.di.module.AppModule.provideApplication()
#Provides #Singleton com.my.package.network.FeedBurnerApi com.my.package.di.module.NetworkModule.provideFeedBurnerApi(android.app.Application)
#Provides #Singleton android.database.sqlite.SQLiteOpenHelper com.my.package.di.module.StorageModule.provideSQLiteOpenHelper(android.app.Application)
#Provides #Singleton com.my.package.storage.Repository com.my.package.di.module.StorageModule.provideRepository(android.database.sqlite.SQLiteOpenHelper)
#Provides #Singleton com.my.package.SharedPreferencesHelper com.my.package.di.module.StorageModule.provideSharedPreferencesHelper(android.app.Application)
So, the question is how I can get the instance of my ActivityComponent?
You can see dependencies between modules below:
Application module:
#Module
public final class AppModule {
private final MyApplication mApplication;
public AppModule(MyApplication application) { ... }
#Provides #Singleton Application provideApplication() { ... }
}
Network module:
#Module(includes = { AppModule.class })
public final class NetworkModule {
#Provides #Singleton FeedBurnerApi provideFeedBurnerApi(Application application) { ... }
#Provides #Singleton Retrofit provideRetrofit() { ... }
}
Storage module:
#Module(includes = { AppModule.class })
public final class StorageModule {
#Provides #Singleton Repository provideRepository(SQLiteOpenHelper sqLiteOpenHelper) { ... }
#Provides #Singleton SQLiteOpenHelper provideSQLiteOpenHelper(Application application) { ... }
#Provides #Singleton SharedPreferencesHelper provideSharedPreferencesHelper(Application application) { ... }
}
Presenters module:
#Module(includes = { AppModule.class, NetworkModule.class, StorageModule.class })
public final class PresentersModule {
#Provides FeedPageViewPresenter provideFeedPageViewPresenter(FeedBurnerApi api, Repository repository, SharedPreferencesHelper preferences) { ... }
#Provides #ActivityScope SlidingTabsViewPresenter provideSlidingTabsViewPresenter(Repository repository) { ... }
}
Application component:
#Singleton
#Component(modules = { AppModule.class, StorageModule.class, NetworkModule.class })
public interface AppComponent {}
Activity component:
#Subcomponent(modules = PresentersModule.class)
#ActivityScope
public interface ActivityComponent {
void inject(FeedPageView view);
void inject(SlidingTabsView view);
}
The problem was in my PresentersModule.
Since Subcomponents have access to entire objects graph from their parents, I don't need to include these dependencies in my sub-module.
So, I changed this code:
#Module(includes = { AppModule.class, NetworkModule.class, StorageModule.class })
public final class PresentersModule { ... }
with this:
#Module
public final class PresentersModule { ... }
and it solved my issue.