Dagger: need help in understanding - android

I have some misunderstandings of the way that dagger works:
There are only two ways to satisfy dependency: whether the #Provide method returns the instance, or the class should have #Singleton annotation, is that right? Must the class constructor have #Inject annotation in the latter case?
As I see, the ObjectGraph generates all the injection stuff. And it's said, that its inject(T instance) should be called to inject fields. However, I can just annotate my field with #Inject and it goes (field's class is #Singletone). ObjectGraph is not needed to satisfy such a dependency, right?
What about injects{} in #Module, what specifically does it give? Plz, provide an example of benefit when you keep all the injectable classes list.

Yes, there are two ways: #Provide methods in modules and #Singleton classes with #Inject at constructor.
Must the class constructor have #Inject annotation in the latter case?
Yes, otherwise object wouldn't be created by Dagger.
I don't think #Singleton with field injection could work. Because #Singleton at class with constructor injection means Dagger is responsible to keep one instance of this class. And it can create this class using constructor injection if all dependencies are satisfied. However, #Singleton with field injection seems misuse to me, cause keeping single instance of this class is user's responsibility now. Dagger can't instantiate this object itself.
Are you sure this configuration compiles and runs? And if it is check #Inject fields, they should be null by my understanding.
injects={} in #Module returns set of classes passed to ObjectGraph.inject(T class) or ObjectGraph.get(Class<T> class). Referring documentation:
It is an error to call ObjectGraph.get(java.lang.Class) or ObjectGraph.inject(T) with a type that isn't listed in the injects set for any of the object graph's modules. Making such a call will trigger an IllegalArgumentException at runtime.
This set helps Dagger to perform static analysis to detects errors and unsatisfied dependencies.
You can find some examples in this thread.

Related

Android Dagger - How to Inject from other module

I've multiple modules in my app, each one of them have his own Module for UI Injection.
Now, i want to have "feed" fragment that have a some pieces from other modules.
So i trying doing it using FragmentContainerView.
I want to Inject the fragment that defined on other module. I'm trying to "include" my other fragment, so i can navigate into this fragment, but if i'm try to inject it, i'm getting this following error:
error: [Dagger/MissingBinding] DotsFragment cannot be provided without an #Inject constructor or an #Provides-annotated method. This type supports members injection but cannot be implicitly provided.
Any idea how can i do it?
HomeModule:
#Module(
includes = [
DotsModule::class
]
)
abstract class HomeModule {
}
DotsModule:
#Module
abstract class DotsModule {
#FragmentScoped
#ContributesAndroidInjector
abstract fun contributeDotsFragment(): DotsFragment
}
HomeFragment:
#Inject
lateinit var dotsFragment: DotsFragment
Just push the Fragment into the container as described in the docs, and don't worry about the injection. You don't even need the explicit inclusion of DotsModule from HomeModule, though you may choose to keep it, especially if HomeModule is in a different Activity. DotsFragment will inject itself in onAttach.
Unlike most classes that use Dagger injection, Fragments can't be injected in their constructors: Android will only call the zero-arg or one-arg Fragment() constructor, so there isn't room for other constructor parameters. When you see your abstract fun contributeDotsFragment() function with #ContributesAndroidInjector, it is not the case that DotsFragment is available on your graph to be constructed or provided. Instead, Dagger does some magic1 that allows the Fragment to have its #Inject-labeled fields populated2 once you call AndroidSupportInjection.inject(this) in onAttach, which might happen in DaggerFragment if you inherit from that. Your Fragment has to assume that anything marked #Inject is null until the Fragment is attached.
Consequently, if you need an instance of DotsFragment, you can just call DotsFragment() or delegate to your FragmentManager's FragmentFactory (which will likely just call DotsFragment()). You can also use overloads to FragmentTransaction.replace(...) that take a Class<Fragment> rather than a Fragment instance.
The Fragment will inject itself later in onAttach, as it would if Android itself called the constructor. If you interact with the Fragment before that, be careful, as its #Inject-annotated fields will not be populated yet. If you need to interact with the Fragment in ways that use Dagger-provided instances, it may be best to create a Bundle that the Fragment can use to initialize itself in onCreate.
1: Behind the scenes, dagger.android creates a code-generated subcomponent instance, probably called DotsModule_BindDotsFragment.DotsFragmentSubcomponent, and registers that subcomponent as an AndroidInjector.Factory<DotsFragment> in a Map. The generated subcomponent receives all the modules you set on the #ContributesAndroidInjector annotation as well as all the scope annotations you set on the abstract fun. In onAttach (yours or DaggerFragment's), the call to AndroidSupportInjection.inject(this) reads from that Map, finds the Injector/Subcomponent, creates an instance, and uses that to inject the Fragment with its own set of #FragmentScope instances.
2 The #Inject-annotated methods will be called too. That's method injection, compared to field injection; the general term is members injection for all of the post-constructor initialization Dagger can do.

Use Dagger2 Android Injection Support without specifying the class name

Is there a way in Dagger2 or in Dagger2 Android Injection support to inject the member instances without specifying the class names of the fragments.
I have a modular project where
The following line is asking to provide a binder Factory for the injectable class.
#Override
public void onAttach(Context context) {
AndroidSupportInjection.inject(this);
super.onAttach(context);
}
But my intention is to provide the injecting members through different modules in the project, where the I wouldn't need to specify the class name of the Fragment at all.
Is this possible in Dagger2 injection or not?
Is there a way in Dagger2 or in Dagger2 Android Injection support to inject the member instances without specifying the class names of the fragments.
tl;dr No. You always need a component that contains a .inject(FragmentInQuestion fragment) method for every Fragment you want to inject.
Dagger uses annotation processing at compile time to resolve all of your .inject(SomeFragment fragment) methods where it looks up fields annotated with #Inject and creates code to inject them.
It needs the actual class name, since it will only generate code for the class used with its fields.
class Fragment {
// no #Inject annotated fields, part of framework!
}
class SomeFragment extens Fragment {
#Inject SomeDependency dep;
}
class OtherFragment extens Fragment {
#Inject OtherDependency dep;
}
Declaring one generic component that injects all your fragments is impossible.
.inject(Fragment fragment) would not inject any properties, since Fragment does not contain any #Inject annotated fields. So neither of the dep fields in the above sample would be provided and they both would be null.
You could create a BaseFragment that contains common objects and write an injection for it, but again, any annotated fields of its children would not be supplied.
You could try some other workarounds, but in the end it would always mean that you'd be injecting or be working with a base type. While this might be feasible in some situations, I don't think it would work on more than some special edge cases.
The Android Injection part of Dagger 2 takes this approach and creates a generic interface that your components have to implement along with a Builder to bind the type in question.
interface AndroidInjector<T> {
inject(T instance)
}
By implementing this interface AndroidInjection can then look up (and create) the correct component and inject your Fragment. For the reasons mentioned above this will always be the actual class of your Fragment, and not some base type.
So by using the Android Injection part you won't be able to use some common base class, and even without it you'd have a hard time.
If you're looking for a way to move out the call of AndroidInjection.inject(this) to somewhere else you should look at the Google Android Architecture Components sample project where they use FragmentLifecycleCallbacks to inject the fragments globally at the right time.

Dagger and mvp - should presenter use dagger for injection

I'm starting to think in mvp, dagger should not be used in the presenter. The usual way to construct dagger is using a global component and have subcomponents for scoping the graph. This global component will take an applicationContext as a parameter on creating the appmodule.java class usually. Having the application context given makes life easier.
That's all fine but if I use a module from the global component or even a subcomponent, the context should be passed in. So that means if I inject the presenter with dagger it will be tied to the applicationContext. THIS MAKES IT DIFFICULT TO TEST as junit. Android code should not be in presenter.
So I'm asking is the best practice to just use dagger in activities fragments broadcast receivers and services only? Speaking in terms of mvp architecture that is. The other solution could be to design another dagger component but not a subcomponent that is not related to appcomponent or an applicationContext. What is the common practice?
UPDATE:
LETS look at a real example:
usually we'd create a appComponent that's close in application override like this:
public class MyApplication extends Application {
private AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = initDagger(this);
}
public AppComponent getAppComponent() {
return appComponent;
}
protected AppComponent initDagger(PomeloApplication application) {
return DaggerAppComponent.builder()
.appModule(new AppModule(application))
.build();
}}
and then for example in the presenter we'd do this in the constructor:
public WelcomePresenter(Context context) {
super(context);
presenterComponent = ((MyApplication) context).getAppComponent();
presenterComponent.inject(this);
}
so because I wanted the application Context for dagger providers I ended up forcing callers to depend on a context Object. Basically, you cant get the AppComponent modules until you get a context so you can cast it as "MyApplication" and inject from the component? So i was thinking why is this dagger pattern making me force the presenter to have a context?
Even if we used a subComponent it would still depend on the AppComponent and then in our test case we'd have to deal with an application context being created to get dagger to inject.
So I think there should be a rule - in MVP prefer manual constructor injection over dagger injection
Because class shouldn’t have knowledge about how it is injected. Something being injected should not care about how it is injected.
dagger.android outlines the problem i am talking about as per the accepted answer.
I also agree with the statement "Not having android related code in your presenter is always a good idea, and dagger does not interfere with that."
First, what I want to draw attention that starting from dagger version 2.10 you can use dagger.android package which simplifies injection in the activities and fragments, so you don't need to use MyApplication casting across the app.
Second, why are you not injecting WelcomePresenter into activity or fragment but vice versa?
Dependency injection is useful for Android components (activities, etc.) primarily because these classes must have a no-arg constructor so the framework can construct instances of them. Any class that isn't required to have a no-arg constructor should simply receive all its dependencies in its constructor.
Edit: A side note about how you're getting a reference to the AppComponent.
Application is-a Context, and the Context returned by Context#getApplicationContext() is usually the Application instance. But this is not necessarily the case. The one place I know of where it may not be the case is in an emulator. So this cast can fail:
presenterComponent = ((MyApplication) context).getAppComponent();
Mutable static fields are evil in general, and even more so on Android. But IMO, the one place you can get away with it is in your Application class. There is only one instance of Application, and no other component will ever exist before Application#onCreate() is called. So it's acceptable to make your AppComponent a static field of MyApplication, initialize it in onCreate(), and provide a static getter, as long as you only call the getter from the lifecycle methods of other components.

No injectable members on android.app.Application. Do you want to add an injectable constructor? required by provideOkHttpClient

I am trying to merge U2020 and Dagger's example android-activity-graphs in the following repo.
However, I am getting compile time error:
Error:(32, 8) error: No injectable members on android.app.Application. Do you want to add an injectable constructor? required by provideOkHttpClient(android.app.Application) for org.kamol.nefete.ActivityModule
You haven't bound Application, you have only bound Context, but somewhere else in your graph, you have some type that depends on Application. Downstream dependencies which need Application will attempt to implicitly bind it because Dagger cannot know that an object which requires Application can be satisfied by #ForApplication Context. Since Application does not have an #Inject constructor nor #Inject fields, Dagger will fail.
You can fix your example by simply adding
#Provides #Singleton Application provideApplicationContext() {
return application;
}
in org.kamol.nefete.AndroidModule
As an aside, I recommend against binding Context as it is too abstract a supertype and too easy to confuse with Activity Contexts, even if you are binding with a #Qualifier annotation, such as #ForApplication Context. I would recommend instead binding Application and having things depend on the more concrete type.

How do I inject an Application in a Transfuse Singleton

How can I create a singleton that gets injected with an application context. Just annotating with #Singleton and then using #Inject on the constructor ends up generating an UnscopedProvider class that will not compile. How can I make an ApplicationScoped provider... or is there another mechanism to accomplish this?
The solution I ended on here is to create an init(Application app) {} method on the Singleton, then inject the Singleton into my #Application annotated class and call the init method using the Application that's injected into that class. I think this is the only way to do this currently.

Categories

Resources