Injecting application context in library module with Dagger 2 - android

I'm building an app with some features: a ContentProvider a SyncAdapter, a Job service and related persistence logic. On top on these there are the Activities with the UI. I'm trying to put all said features in a separate library module, because in theory their logic is stand-alone and would be reusable by any application.
Now comes Dagger2. The first node (main Component) of my library's dependency graph does need to provide Context, and this Context has to be injected from the Application, because the library scope has the same lifecycle of the application. To be self contained, obviously, my library should not directly use my Application class.
These are the possibilities I thought of:
Build the library's main Component in my Application and store it in a global static class/enum as suggested here but I'm concerned that using such a static reference could be an anti-pattern.
Pack in the library an Application class which builds the library scoped Component, cast app context to this class in the library to use the component and then extend this Application class on the main app. This works, but if there's more than one library it's not viable anymore.
Use the factory pattern: put provision methods in the library component that provide the factory which in turn is given the locally available context as a parameter. (As explained here). This seems viable, although it adds extra complexity.
Last but non the least, give up trying to modularize the components, since being dependent on the application context breaks the concept of modularity.
What is the correct way to do this?

Dagger 2 for Android comes to the rescue. It provides the concept of AndroidInjector, which is a Component that can be used to inject an instance in a static way, without having to know the dependency provider. Moreover, using the Dagger- prefixed classes provided out of the box, the injected dependencies look like coming from nowhere. Awesome.
All you have to do is declare in the library a top-level Module which is installed in the Application Component. This Module will provide all the dependencies and the SubComponents needed by the library, which will automatically inherit the #AppContext Context that you seeded in the dependency graph, ready to be injected anywhere in you library, as well as every dependency you provide through the main Application Component.
Here's a short example (written in Kotlin):
#Component(modules = [
AndroidSupportInjectionModule::class,
AppModule::class,
LibraryModule::class //plug-in the library to the dependency graph
])
#Singleton
interface AppComponent : AndroidInjector<App> {
#Component.Builder
abstract class Builder : AndroidInjector.Builder<App>() {
#BindsInstance
abstract fun appContext(#AppContext context: Context)
override fun seedInstance(instance: App) {
appContext(instance)
}
}
}
Edit: extended examples
An example of the Application subclass:
// DaggerApplication provides out-of-the-box support to all the AndroidInjectors.
// See the class' code to understand the magic.
public class App extends DaggerApplication {
#Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
// We only provide its own Injector, the Application Injector,
// that is the previous AppComponent
return DaggerAppComponent.builder().create(this);
}
And in your Android library:
#Module
public abstract class LibraryModule {
#ContributesAndroidInjector
public abstract LibraryActivity contributeLibraryActivityInjector();
}
public class LibraryActivity extends DaggerAppCompatActivity {
#Inject
#AppContext
Context appContext;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceSate);
// here you automagically have your injected application context!
ExternalSingleton.getInstance(appContext)
}
}

Related

Is Dagger component bound to the object that instantiate it?

I have been using Dagger 2 in my project. I understand that the lifetime of scoped object is the same as the lifetime of component (with the same scope). What about the lifetime of component then?
For example, I have a component:
#MyApp
#Component(modules = {
ApplicationModule.class})
public interface ApplicationComponent {
// Injection methods
void inject(MyApplication mainApplication);
}
I build component by:
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
buildApplicationComponent();
mApplicationComponent.inject(this);
}
private void buildApplicationComponent() {
mApplicationComponent = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.build();
}
}
Currently, the code to build component is in Application class. But
is it so that if I build component in Fragment the ApplicationComponent would have the same lifetime as the fragment & if I execute it in Application class the component would have the same lifetime as the whole application? Or how the lifetime of component is defined?
It is plain Java. You have object no matter where. The object provides you dependencies. If you use the same instance you will have the same dependencies or basically the same "Scope/lifetime". If you create somewhere new object it means new other object\dependencies it can provide so "another Scope/lifetime". If you share the object between different objects (You create it in the Application class, but reuse it in another fragment) you are in the "same Scope/lifetime".
But in general I see only "old way" of using Dagger 2 here. This is how your classes should end up everywhere. No need to find reference to any Components and etc or try to instantiate them on your own or clear them on your own in Fragments or Actvities. You have some code for Dagger 2 in one place and then some Annotations in you classes "which do the real work of your project". No need for complicated "connection" between the two parts and I see here a lot of cases like this which are part of tutorials that date back to 2016-2017...
class Repostory #Inject constructor(
private val dependency1: Dependency1
) {}
class Activity or Fragment {
#Inject lateinit var dependency2: Dependency2
}
This is a great example for right usage of Dagger2.
https://github.com/google/iosched
Here is an article about the app:
https://medium.com/#JoseAlcerreca
Jose Alcerreca is one of the Google Lead Devs responsible for creating the guidelines to write Android apps.
#Module
abstract class ActivityBindingModule {
#ActivityScoped
#ContributesAndroidInjector(modules = [MainActivityModule::class])
internal abstract fun mainActivity(): MainActivity
}
Just try to compile something like this. There is the exaxt same class in the app I gave you. Check the generated code and also the docs. If you see ContributesAndroidInjector there is explained:
Generates an {#link AndroidInjector} for the return type of this method. The injector is
implemented with a {#link dagger.Subcomponent} and will be a child of the {#link dagger.Module}'s component.
If you check the AndroidInjector docs you will see that it is the one that is injectin the Activity. And there it will be the implementation:
#Subcomponent(modules = {MainActivityModule.class})
#ActivityScoped
public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> {
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MainActivity> {}
}
So in general these annotations do the magic for you when a new activity starts. They create the Subcomponent, they use it and when the Activity Lifecycle ends the Subcomponent is also dead. Dagger clears the reference. Dagger knows how Activities work or Fragments and etc. There was the old way when you craete the component in the Activity, use it, call inject on your own. Or put some component in the AppClass and then clear it on your onw. But now there is a bigger amount of annotations.

Stuck attempting to implement the official Dagger strategy to avoid cumbersome code

In my efforts to follow the good and official advice for injecting and avoiding cumbersome code (which I had) from the authors themselves, I ran into a wall when trying to use the support library.
According to the article:
AppCompat users should continue to implement AndroidInjector.Factory<? extends Activity> and not <? extends AppCompatActivity> (or FragmentActivity).
I'm sticking to an MVP architecture where views are always Fragments and I don't want to involve my Activity in any DI business, but I wonder if it's necessary for this to work but so far I haven't been able to. If I skip the whole support thing, the app crashes at runtime because the instance of the fragment is support (in case it's not obvious). Then I went into the task of trying to try to implement HasSupportFragmentInjector instead of HasFragmentInjector with a whole bunch of changes due to compile errors my mind has forgotten for the sake of my mental health. After a while I come to a point of thinking how can a non-support Activity host a support fragment. Ah! Those tricky wildcards. But no matter how I've tried to follow the advice, I can't come up with a way without an EmptyModule that I also would need to setup in the Activity so it would be visible to the fragment by dagger and its (really, for me still, magic). Why I haven't tried it? I might as well have, but I'm tired of hopeless changes and I need help at this point.
AppModule.kt
#Singleton
#dagger.Module
class AppModule(val application: Application) {
#Provides #Singleton fun application(): Application = application
...
}
AppComponent.java
#ApplicationScope
#Singleton
#Component(modules = {
AndroidSupportInjectionModule.class,
...
FooFragmentModule.class,
})
public interface AppComponent {
Application app();
...
void inject(MyApp app);
}
MyApp.java
public class MyApp extends Application implements HasActivityInjector {
private AppComponent component;
public AppComponent someWayToReturnAppComponent() {
...
}
#Inject DispatchingAndroidInjector<Activity> dispatchingActivityInjector;
#Override
public void onCreate() {
component = DaggerAppComponent.builder()
.appModule(new AppModule(this))
// more app-scoped modules
.build();
component.inject(this);
}
#Override
public AndroidInjector<Activity> activityInjector() {
return dispatchingActivityInjector;
}
}
MainActivity.java
public abstract class MainActivity extends AppCompatActivity implements HasSupportFragmentInjector {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayout()); // inflate the fragment via XML here
}
#Inject DispatchingAndroidInjector<Fragment> dispatchingFragmentInjector;
#Override
public AndroidInjector<Fragment> fragmentInjector() {
return dispatchingFragmentInjector;
}
}
FooFragmentComponent.java
#Subcomponent
public interface FooFragmentComponent extends AndroidInjector<FooFragment> {
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<FooFragment> {}
}
FooFragmentModule.kt
#dagger.Module(subcomponents = {FooFragmentComponent.class})
public abstract class FooFragmentModule {
#Binds
#IntoMap
#FragmentKey(FooFragment.class)
abstract AndroidInjector.Factory<? extends Fragment> bindFragmentInjectorFactory(FooFragmentComponent.Builder builder);
#ActivityScope
abstract FooFragment contributeFooFragmentInjector();
#Provides
static FooPresenter presenter() {
return new FooPresenter();
}
}
FooFragment
public class FooFragment extends Fragment implements SomeView {
#Inject FooPresenter presenter;
}
OK. At this point, and going back to
AppCompat users should continue to implement AndroidInjector.Factory<? extends Activity>
I've had no need (and willingly opposing) to use it, only for the fragment. Do I really need to setup a module and component for it or am I missing something?
EDIT
After following EpicPandaForce's advice of using AndroidSupportInjectionModule, Dagger now complains that
FragmentKey methods should bind dagger.android.AndroidInjector.Factory<? extends android.app.Fragment>, not dagger.android.AndroidInjector.Factory<? extends android.support.v4.app.Fragment>.
As #EpicPandaForce mentioned in the comments, you need to use AndroidSupportInjectionModule for support Fragments. You'll also need to use the FragmentKey in dagger.android.support, not the one in dagger.android. That should get you past the problem in your edit.
To your broader point, support Fragments do not extend base Fragments (which are deprecated anyway in API 28 and beyond). This paints them in contrast to AppCompatActivity and its superclass, the support library's FragmentActivity, which both extend the framework Activity as introduced in Android API level 1. Thus, whether you're using support Fragments or built-in Fragments, you might not have a parent AppCompatActivity, but you'll always have an Activity of some sort. This is important because Android reserves the right to instantiate your Fragment using its necessary public no-arg constructor, which means that the Fragment can only self-inject using things that it can find inside onAttach (i.e. its parent fragments, its Activity, or its Application).
dagger.android is unconcerned whether your Activity is an AppCompatActivity because it does not use the Activity other than looking for its own injector. You can see that in the AndroidSupportInjection.findHasFragmentInjector private method, which checks (in order) the hierarchy of parent fragments, then the Activity, then the Application. Consequently, even though practically speaking Support Fragments will only function properly on support Activities, dagger.android can bind its keys based on the superclass Activity because there's no reason to differentiate them and set up two separate maps (Activity vs AppCompatActivity). Even if there were a separation like that, you could bind AppCompatActivity injectors into your Activity map, and everything would get terribly confusing.
You should also take from that search order that if you do not have Activity-scoped bindings, you do not need to create an Activity-scoped component; you can have your Application implement HasSupportFragmentInjector, install your FooFragmentModule directly into AppComponent, and remove HasSupportFragmentInjector from your MainActivity. This is atypical only because most apps have some sense of Activity state or controllers that should be injectable (even just injecting the Activity instance itself, or its Context or Resources). If you only have your #ActivityScope annotation because you're trying to make this work, you can skip that step entirely and only use an Application component and several Fragment subcomponents. However, I think it is very likely that you will eventually need #ActivityScope, so creating a Component for it early-on is pretty reasonable.

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 2 - how to avoid code repetition for injecting dependencies that require activity context

The project I'm working on has a number of utility classes that require activity context.
I don't want to have to declare a new #Provides method for each activity that uses the dependency. i.e. I don't want this:
#Provides
static Navigator providesNavigator(ActivityOne activity) {
returns new Navigator(activity);
}
// ...and in another module
#Provides
static Navigator providesNavigator(ActivityTwo activity) {
returns new Navigator(activity);
}
So instead I declare these utilities in a single ActivityUtilitiesModule and pass our BaseActivity which all other activities extend. Now i don't have to declare my Navigator dependency x number of times.
#Provides
static Navigator(BaseActivity activity) {
return new Navigator(activity);
}
However, Dagger does not know how to satisfy the dependency for BaseActivity. This means for every Activity i need to create a provides method that will satisfy the BaseActivity dependency with the specific Activity being used. e.g.:
#Provides
static BaseActivity providesBaseActivity(ActivityOne activity) {
return activity;
}
This is better - I only need to repeat this one provider per activity, rather than repeating a provider for every utility class per activity, but it still feels like an unwelcome additional step in Dagger set up, and another thing that makes the code harder to understand.
Is there a pattern which allows me to avoid having to supply this BaseActivity provider per activity?
Please use Constructor Injection. Having provide* methods that only call the constructor are only noise and code to maintain. Add #Inject to the class constructor and possible scopes onto the class.
#SomePossibleScope
class Navigator {
#Inject
Navigator(Activity activity) { }
}
If you do this, you probably don't need your ActivityUtilitiesModule at all.
If your class depends on an Activity or BaseActivity then you need to provide it. And yes, you will have to tell Dagger about it in some way.
If you were to use an abstract class or interface you should make use of #Binds instead.
#Binds BaseActivity bindsBaseActivity(ActivityOne activity);
Compared to #Provides Dagger might optimize this code further, reducing the number of method calls and object creations, as well as a few less lines of code.
I don't know why your Utils depend on the Activity, but if they would only need a Context then you could also just provide the application context to them without a need to bind or provide your actual Activity.
I personally just bind the current Activity to the types it implements using the syntax above. And if you're using Constructor Injection properly that's more often than not the only lines of code that you'll find in my modules, making them very readable.

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.

Categories

Resources