I really need help with solving the following error:
error: [Dagger/MissingBinding] prj.view.fragment.FragmentA cannot be provided without an #Inject constructor or an #Provides-annotated method. This type supports members injection but cannot be implicitly provided.
A binding with matching key exists in component: prj.di.module.FragmentsModule_ContributeFragmentA.FragmentASubcomponent
prj.view.fragment.FragmentA is injected at
prj.view.activity.MainActivity.fragmentA
prj.view.activity.MainActivity is injected at
dagger.android.AndroidInjector.inject(T) [prj.di.component.ApplicationComponent → prj.di.module.ActivitiesModule_ContributeMainActivity.MainActivitySubcomponent]
I tried this approach but for some reason, this does not work for me, and here are my classes:
#ApplicationScope
#Component(modules = {ApplicationContextModule.class, RetrofitModule.class, ActivitiesModule.class, AndroidSupportInjectionModule.class})
public interface ApplicationComponent extends AndroidInjector<MyApplication> {
#Component.Factory
interface Factory extends AndroidInjector.Factory<MyApplication> {}
}
#Module
public abstract class ApplicationContextModule {
#Binds
#ApplicationScope
#ApplicationContext
abstract Context bindsContext(MyApplication context);
}
#Module
public abstract class ActivitiesModule {
#ActivityScope
#ContributesAndroidInjector(modules = {MainActivityContextModule.class, FragmentsModule.class})
abstract MainActivity contributeMainActivity();
}
#Module
public class MainActivityContextModule {
#Provides
#ActivityScope
#ActivityContext
Context provideContext(MainActivity context){
return context;
}
}
#Module
public abstract class FragmentsModule {
#FragmentScope
#ContributesAndroidInjector()
abstract FragmentA contributeFragmentA();
}
// BaseActivity extends DaggerAppCompatActivity
public class MainActivity extends BaseActivity {
#Inject FragmentA fragmentA;
#Inject
#ApplicationContext
public Context applicationContext;
#Inject
#ActivityContext
public Context activityContext;
...
public class FragmentA extends DaggerFragment {
#Inject
DispatchingAndroidInjector<Fragment> childFragmentInjector;
#Inject
#ActivityContext
Context activityContext;
...
So as you can see I implemented a similar logic as proposed in the link provided above, but for some reason that is not working. Please, help me to understand what's wrong...
I added an empty constructor annotated with #Inject for the Fragment and it worked... Don't completely understand why Activity injection works without it and Fragment injection not but the problem is solved
If you have ApplicationScope > ActivityScope > FragmentScope then your Activity (ActivityScope) can't directly inject a Fragment (FragmentScope), because the Fragment isn't part of the Activity's graph, the same way you can't inject that Activity in your Application. Subcomponents can inject objects available on the parent component, but not the other way around.
The simplest solution would be to not inject the Fragment and just create it like you usually would. You can inject the Activity into the Fragment (parent > child scope) and use any other Activity / Fragment scoped objects there without further problems.
Related
I have a class called AlertManager which requires Activity instance to show Toast and AlertDialog.
class AlertManager #Inject constructor(private val activity: Activity) {
fun showToast(message: String) {
Toast.makeText(activity, message, Toast.LENGTH_SHORT).show()
}
}
Now, I want AlertManager as dependency in two activities HomeActivity & ProductsActivity. Currently I have created modules for each Activity like:
#Module
class HomeActivityModule {
#Provides
#ActivityContext
fun provideAlertManager(activity: HomeActivity) = AlertManager(activity)
}
And
#Module
class ProductsActivityModule {
#Provides
#ActivityContext
fun provideAlertManager(activity: ProductsActivity) = AlertManager(activity)
}
And binding them with Dagger like
#Module
abstract class ActivityProvider {
#ContributesAndroidInjector(modules = [HomeActivityModule::class])
#ActivityContext
abstract fun bindHomeActivity(): HomeActivity
#ContributesAndroidInjector(modules = [ProductsActivityModule::class])
#ActivityContext
abstract fun bindProductsActivity(): ProductsActivity
}
Now my questions are:
1) How can I avoid creating modules for each activities and have common ActivityModule which I can bind with whatever Activity I want?
2) Let's say I have a fragment called HomeFragment inside HomeActivity, then how can I inject the same AlertManager instance of HomeActivity inside the fragment?
I am stuck here since quite long and have tried to find a lot over internet but I am unable to find any blog or guide which can help me to achieve what I am looking for. If someone can point me in right direction, I'll be grateful.
1) How can I avoid creating modules for each activities and have common ActivityModule which I can bind with whatever Activity I want?
You can have some sort of AlertManagerModule where you add generic activity.
#Provides
fun provideAlertManager(activity: Activity) = AlertManager(activity)
You still will have to make individual activity modules. One change you can make is:
#Module
abstract class HomeActivityModule {
#Binds
abstract fun providesActivity(activity: HomeActivity) : Activity
}
And then you can add them to the ActivityProvider class:
#Module
abstract class ActivityProvider {
#ContributesAndroidInjector(modules = [HomeActivityModule::class, AlertManagerModule::class])
abstract fun bindHomeActivity(): HomeActivity
#ContributesAndroidInjector(modules = [ProductsActivityModule::class, AlertManagerModule::class])
abstract fun bindProductsActivity(): ProductsActivity
}
2) Let's say I have a fragment called HomeFragment inside HomeActivity, then how can I inject the same AlertManager instance of HomeActivity inside the fragment?
Since you're using DaggerActivity and most likely using DaggerFragment, the fragment instantiated in the HomeFragment can directly get the AlertManager by simply using the #Inject annotation in the fragment provided you add in the HomeActivityModule:
#Module
abstract class HomeActivityModule {
#Binds
abstract fun providesActivity(activity: HomeActivity) : Activity
#FragmentScope
#ContributesAndroidInjector
abstract fun providesHomeFragment() : HomeFragment;
}
ArticlesContract.Presenter is a new instance in Adapter, which is different with ArticleListFragment, so my data was lost ! I have no idea why I got two different instances:
#Module
public class ArticleListFragmentModule {
#Provides
ArticlesContract.Presenter provideArticlesPresenter(ArticlesPresenter presenter) {
return presenter;
}
}
public class ArticleListFragment extends DaggerFragment implements ArticlesContract.View {
#Inject
ArticlesContract.Presenter mPresenter; //one instance
}
public class ArticlesAdapter extends RecyclerView.Adapter<ArticleViewHolder> {
#Inject
ArticlesContract.Presenter mPresenter; //another different instance
}
2018-05-16 UPDATED: this issues is fixed by following #Fred answer : lack a scope and managing this scope:
#Scope
#Documented
#Retention(RetentionPolicy.RUNTIME)
public #interface ArticlesScope {
}
#Module
public abstract class ActivityBuilder {
#ContributesAndroidInjector(modules = ArticleListFragmentModule.class)
#ArticleListScope
abstract ArticleListFragment bindArticleListFragment();
}
#Module
public class ArticleListFragmentModule {
/**
* Provide dependency for interface.
* Interface cannot be annotated with #Inject, otherwise it will cause, error: ArticlesContract.Presenter cannot be provided without an #Provides- or #Produces-annotated method.
*/
#Provides
#ArticleListScope
ArticlesContract.Presenter provideArticlesPresenter(ArticlesPresenter presenter) {
return presenter;
}
}
Scoping with #ContributesAndroidInjector, refer to Dagger 2 Annotations: #Binds & #ContributesAndroidInjector
This is because you lack a scope and managing this scope. You need to create a scope yourself or use one provided by dagger already. Here since it's a presenter it can be the Reusable scope I guess or Singleton. However, Singleton has a performance impact that might not be desirable.
The next important thing is that you need to understand that while the component's instance is the same the provided binding will be the same (excluding the case of the Reusable scope). In other words, scopes provide a way of telling dagger - "while this component is alive and it's scope is X then all instances scoped with X will be the same". Here's what I mean in code:
#Scope
#Documented
#Retention(RUNTIME)
public #interface PresenterScope {}
#Module
public class ArticleListFragmentModule {
#Provides
#PresenterScope
ArticlesContract.Presenter provideArticlesPresenter(ArticlesPresenter presenter) {
return presenter;
}
}
I don't know how you've set up your component, but you'd have to annotate it also with the PresenterScope. Now, it's just a matter of making sure that when you inject ArticleListFragment and ArticlesAdapter you'll be using the same instance of the component. If you rebuild the component than the instance of the presenter will be different.
Remember that Reusable is a bit different, but it should suit your needs here since the presenter should hold no state.
Hope this helps
In Dagger 2 Android, I was really liking the new simplified API, but I now require a little bit of customisation and it's making my head hurt.
I have a very common scenario: an app with some activities and every dependency is injected.
I'll describe my current implementation first and the problem afterwards. Bear with me for a second, please.
I started by creating the regular ActivitiesModule
#Module
abstract class ActivitiesModule {
#PerActivity
#ContributesAndroidInjector(modules = MainActivityModule.class)
abstract MainActivity bindMainActivity();
// More activities here, all with the scope #PerActivity
}
and then my MainActivityModule.java (and all the other activity modules) look like this
#Module
public abstract class MainActivityModule {
#Binds
#ActivityContext
#PerActivity
abstract Context provideActivityContext(MainActivity mainActivity);
#Binds
#PerActivity
abstract Activity provideActivity(MainActivity mainActivity);
#Binds
#PerActivity
abstract MainMvpView provideView(MainActivity mainActivity);
}
This is perfect, because every time I needed the activity or context in a class, I could just do this, without tightly coupling my object to a specific activity.
#PerActivity
public class MainPresenter {
private final MainMvpView view;
#Inject
MainPresenter(MainMvpView view) {
this.view = view;
}
}
This is great, because I don't need to declare a #Subcomponent if I don't have to. However, now I want to inject a custom view into my MainActivity subcomponent, and I'm having issues going back to the old dagger.android way of doing things.
So far, I replaced the ContributesAndroidInjector with the old way, like this
#Binds
#IntoMap
#ActivityKey(MainActivity.class)
abstract AndroidInjector.Factory<? extends Activity>
bindMainActivityInjectorFactory(MainActivitySubcomponent.Builder builder);
and I created a new MainActivitySubcomponent that looks like this
#PerActivity
#Subcomponent
public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> {
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MainActivity> {}
}
The module pretty much stayed the same. The problem is that I'm getting lots of errors that say that Activity and Context are bound multiple times.
So my 2 questions are:
Is it not possible to have #Binds with Activity and Context if they are scoped so that I can keep things totally decoupled?
Is there an alternative to do view injections using the latest from dagger.android?
Any suggestions will be much appreciated.
I'm using Dagger Android to link dependencies in my Android application.
I have 2 Activities, both have their own Scope (#ActivityScoped).
Here is my code so far :
MyApplication.java
public class MyApplication extends Application implements HasActivityInjector {
#Inject DispatchingAndroidInjector<Activity> activityDispatchingAndroidInjector;
#Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.builder()
.application(this)
.build()
.inject(this);
}
#Override
public AndroidInjector<Activity> activityInjector() {
return activityDispatchingAndroidInjector;
}
}
AppComponent.java
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
AppModule.class,
ActivityBindingModule.class
})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application app);
AppComponent build();
}
void inject(MyApplication app);
}
ActivityBindingsModule.java
#Module(subcomponents = ActivityComponent.class)
public abstract class ActivityBindingModule {
#ActivityScoped
#ContributesAndroidInjector
public abstract MainActivity contributeMainActivityInjector();
#ActivityScoped
#ContributesAndroidInjector
public abstract SecondActivity contributeSecondActivityInjector();
}
Let's say I have a class MyClass tied to ActivityScoped that I want to inject into both Activities :
MyClass.java
#ActivityScoped
public class MyClass {
#Inject
public MyClass(Activity activity) {
// Do something with the activity instance...
}
}
What I want to achieve is to inject into MyClass the Activity instance of the enclosing scope.
For example, if MyClass is injected into ActivityA, then the activity constructor argument of MyClass should be an instance of ActivityA.
What I tried :
#Binds annotated method to inject ActivityA and ActivityB as Activity. This doesn't work since Dagger does not allow binding multiple implementations to the same base type.
Map Multibindings doesn't suit my needs : I don't want MyClass to know about the Activity in which it is injected.
Maybe I'm missing something, as dependency injection is rather hard to setup. How can I do that with Dagger Android ?
#Binds annotated method to inject ActivityA and ActivityB as Activity. This doesn't work since Dagger does not allow binding multiple implementations to the same base type.
That's the right way to do so. You only get an error when you try to bind multiple implementations to the same type in the same component. So you should not bind both within your AppComponent, but to the #ActivityScoped subcomponents.
You have to create a module for each Activity, A and B, that binds the implementation to Activity. Then you just add that module to the Subcomponent that Dagger creates for you.
#ActivityScoped
#ContributesAndroidInjector(modules={MainActivityBindingsModule.class})
public abstract MainActivity contributeMainActivityInjector();
That way the binding only exists within the #ActivityScoped component and you should be able to repeat it.
I have been trying to, unsuccessfully, inject the Activity in a class ViewUtils. I have followed a couple of different posts but I can't seem to understand what am I missing in my implementation.
I know this is probably a repetition of the posts below and I really apologize for that but I honestly cannot see what am I missing. These are the posts I've found:
Dagger 2.10 Android subcomponents and builders
How to create custom scoped modules in dagger 2.10
https://google.github.io/dagger/subcomponents.html
My implementation is as follows:
AppComponent
#Component(modules = {
AppModule.class, AndroidSupportInjectionModule.class, ActivityBindingModule.class
}) #Singleton public interface AppComponent extends AndroidInjector<EmblyApp> {
#Component.Builder abstract class Builder extends AndroidInjector.Builder<EmblyApp> {}
}
ActivityBindingModule
#Module public abstract class ActivityBindingModule {
#ContributesAndroidInjector
abstract LoginActivity loginActivity();
}
LoginSubcomponent
#Subcomponent(modules = LoginSubcomponent.LoginActivityModule.class)
public interface LoginSubcomponent extends AndroidInjector<LoginActivity> {
#Subcomponent.Builder abstract class Builder extends AndroidInjector.Builder<LoginActivity> {}
#Module abstract class LoginActivityModule {
#Binds abstract Activity bindActivity(LoginActivity activity);
#Provides #ActivityScope static ViewUtils viewUtils(Activity activity) {
return new ViewUtils(activity);
}
}
}
ViewUtils
public class ViewUtils {
private final Activity activity;
#Inject public ViewUtils(Activity activity) {
this.activity = activity;
}
}
And the error i'm getting is:
Error:(14, 22) error: [dagger.android.AndroidInjector.inject(T)] android.app.Activity cannot be provided without an #Inject constructor or from an #Provides-annotated method.
android.app.Activity is injected at
com.emblyapp.app.ui.helpers.ViewUtils.<init>(activity)
com.emblyapp.app.ui.helpers.ViewUtils is injected at
com.emblyapp.app.ui.authentication.login.LoginActivity.viewUtils
com.emblyapp.app.ui.authentication.login.LoginActivity is injected at
dagger.android.AndroidInjector.inject(arg0)
What is wrong in here? Thanks for the help!
Edit: I forgot to mention my LoginActivity has the injection with the AndroidInjection
#Override protected void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
}
As specified in dagger android documentation:
Pro-tip: If your subcomponent and its builder have no other methods or supertypes than the ones mentioned in step #2, you can use #ContributesAndroidInjector to generate them for you. Instead of steps 2 and 3, add an abstract module method that returns your activity, annotate it with #ContributesAndroidInjector, and specify the modules you want to install into the subcomponent. If the subcomponent needs scopes, apply the scope annotations to the method as well.
Thus, we can get rid of LoginSubcomponent and perform following changes in ActivityBindingModule:
#Module
public abstract class ActivityBindingModule {
#ActivityScope
#ContributesAndroidInjector(modules = LoginActivityModule.class)
abstract LoginActivity loginActivity();
}
LoginActivityModule.java
#Module
abstract class LoginActivityModule {
#Binds
abstract Activity bindActivity(LoginActivity activity);
#Provides
#ActivityScope
static ViewUtils viewUtils(Activity activity) {
return new ViewUtils(activity);
}
}
Your custom application class:
public class MyApp extends DaggerApplication {
#Inject
DispatchingAndroidInjector dispatchingActivityInjector;
#Override
protected AndroidInjector applicationInjector() {
return DaggerAppComponent.builder().create(this);
}
}