Dagger and mvp - should presenter use dagger for injection - android

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.

Related

one object if injected into 2 subcomponents under same custom scope, every time new instance is created of that object

one object if injected into 2 subcomponents under same custom scope, every time new instance is created of that object. I want same instance to be passed to all subcomponents
this is the module
#CustomScope
#Module
public class EventBusModule {
PublishSubject<Boolean> bus = PublishSubject.create();
#CustomScope
#Provides
public PublishSubject<Boolean> provideRxBus() {
return bus;
}
}
these are my subcomponents
#Module
public abstract class ActivityBindingModule {
#CustomScope
#ContributesAndroidInjector(modules = {HomeActivityModule.class,
EwayBillFragmentProvider.class, EventBusModule.class})
abstract HomeActivity mainActivity();
#CustomScope
#ContributesAndroidInjector(modules =
{EwayBillDetailActivityModule.class, EventBusModule.class})
abstract EwayBillDetailActivity ewayBillDetailActivity();
}
these subcomponents are written inside ActivityBindingModule which is added to my application component. Now I want same instance of my PublishSubject object in both the subcomponents, I am fairly new to dagger and I want to know what am I doing wrong?
You'll need to move your bus into Application scope, which typically means annotating it with #Singleton (if that's how you've annotated your top-level component that ActivityBindingModule is installed into). You'll also need to move your method into a Module installed on that component, which might as well be ActivityBindingModule.
#Module
public abstract class ActivityBindingModule {
#Singleton
#Provides
public PublishSubject<Boolean> provideRxBus() {
// Dagger stores the instance in your Application component, so you don't have to.
return PublishSubject.create();
}
/* ... your #ContributesAndroidInjector Activity bindings remain here ... */
}
First, an explanation of what you see: #ContributesAndroidInjector creates a subcomponent for each object it annotates, marked with the scope annotations and modules you put on the #ContributesAndroidInjector method and annotation, so that your call to AndroidInjection.inject(this) in onCreate creates a new instance of that subcomponent and uses it to inject the Activity instance.
Your #CustomScope (which may be better-named as #ActivityScope here) on the #Provides PublishSubject<Boolean> method means that your instance will share the same lifecycle as the component that is also annotated with that scope annotation. Here, that's each automatically-generated subcomponent. Furthermore, because your Module is a non-abstract class with public no-arg constructor, Dagger will automatically create a new instance every time it creates a Component that requires your module, which means a different bus for each Activity instance. (It can't and won't do so for Modules that are abstract classes or interfaces.)
You want your bus object to be the same instance between Activities, which means that #CustomScope/#ActivityScope is much too short: You want the object to outlast any single Activity's lifecycle. This means that you'll either need to store the instance elsewhere and pass it into each Activity, or you'll need to store the instance in your Application component itself. I'd recommend the latter, because this is one of the problems Dagger was created to solve, and because this will automatically make the bus available across your application: Dagger subcomponents inherit access to all of the bindings in their parent components. That gives the code you see above. (Note that by doing this, you'll keep the instance of PublishSubject around even when there is no Activity showing, when your application is running in the background; if you want the same instance between Activities, this is a necessary consequence, but choose this carefully to avoid too much background memory use.)
One alternative is that you keep track of the bus instance yourself, and insert it into each Activity. You could do this by having your Module take a parameter, but that is rather tricky to do with dagger.android (which powers #ContributesAndroidInjector). You could also write a #Provides method that delegates to a WeakReference, or use the #Singleton technique above to write a holder that temporarily stores your bus between Activities. However, because Android keeps a lot of control over your transitions between Activities and the Activity lifecycle, it may be the best you can do to keep the bus in #Singleton scope as I did in the code above.

No injector factory bound with #ContributesAndroidInjector

Please don't mark this duplicate, I've read all the other answers about this issue. I'm not asking what the issue means, I'm asking why this particular code produces this error.
I'm trying to make an Activity component/injector that uses SessionComponent as its parent:
AppComponent:
#Singleton
#Component(modules = [AppModule::class, AndroidSupportInjectionModule::class])
interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(ltiApp: LTIApp): Builder
fun build(): AppComponent
}
SessionComponent:
#SessionScope
#Component(
dependencies = [AppComponent::class],
modules = [SessionModule::class, CommentaryModule::class, EducationCenterModule::class])
interface SessionComponent {
EducationCenterModule
#dagger.Module
abstract class EducationCenterModule {
#EducationScope
#ContributesAndroidInjector()
abstract fun educationCenterActivity(): EducationCenterActivity
}
How come I get an error for injector factory even though I have #ContributesAndroidInjector inside a Module?
Caused by: java.lang.IllegalArgumentException: No injector factory
bound for
Class
If possible, move your #ContributesAndroidInjector into your AppModule, which will likely involve some refactoring between AppComponent and SessionComponent.
dagger.android injects Activity instances by calling getApplication() on the Activity, casting that to a HasActivityInjector, and calling activityInjector().inject(activity) on it (code). In turn, apps that use DaggerApplication (or the code on the dagger.android how-to page) will inject a DispatchingAndroidInjector<Activity>, which injects a Map<Class, AndroidInjector.Builder> that is built using multibindings. Though it's possible to inject into this map directly, you may also use #ContributesAndroidInjector (as you have done here) as a shortcut that produces the multibinding and subcomponent.
Though you have #ContributesAndroidInjector bound inside a #Module, you have two top-level components: AppComponent and SessionComponent. dagger.android is not prepared for this: Your Application likely uses AppComponent for its injection, and because you haven't installed EducationCenterModule into AppComponent, the multibound map will not contain the binding that your #ContributesAndroidInjector method installs.
This probably requires some refactoring, but for important reasons: Through intents, back stacks, and activity management, Android reserves the right to recreate your Activity instance whenever it wants to. Though your Application subclass likely guarantees that an AppComponent will exist by then (by creating and storing that component within onCreate), there is no such guarantee that your SessionComponent will exist, nor any established way for an Android-created Activity instance to find the SessionComponent that it can use.
The most common way to solve this problem is to separate the Android lifecycle from your business logic, such that dagger.android manages your Android components on their own lifecycle, and those Android components create/destroy SessionComponent and other business logic classes as needed. This may also be important if you ever require Service, ContentProvider, or BroadcastReceiver classes, as those will definitely only have access to the application, and may restore or create sessions of their own. Finally, this also means that a Session will necessarily last longer than an Activity instance, which might mean that your Session will not be garbage collected until Android destroys your Activity, and may also mean that you have multiple concurrent SessionComponent instances.
Edit/elaboration: First and foremost you'll need to decide whether sessions outlive Activities, Activities outlive Sessions, or neither. I bet it's "neither", which is fine: at that point I'd write an injectable #Singleton SessionManager that goes inside AppComponent and manages the creation, recreation, and fetching of SessionComponent. I'd then try to divide it so most of the business logic is on the SessionComponent side, and by calling sessionManager.get().getBusinessObject() you can access it from SessionComponent. This also works well to keep you honest, since the business logic side might be easy to unit test using Robolectric or Java, while the Android side might require instrumentation tests in an emulator. And, of course, you can use #Provides methods in your AppComponent modules to pull out your SessionComponent, BusinessObject, or any other relevant instance out of the SessionManager side.
However, if you are 100% sure that you want SessionComponent to be the container for your Activity, and that you don't mind managing the session creation and deletion in your Application subclass. If that's the case, then rather than using DaggerApplication, you can write your Application subclass to implement HasActivityInjector (etc), and delegate the activityInjector() method to a class on a SessionComponent instance that you create. This means that AppComponent would no longer include AndroidSupportInjectionModule, because you no longer inject DispatchingAndroidInjector or its Map. However, this is an unusual structure with implications for your application, so you should consider your component structure carefully before proceeding and document it heavily if you choose the non-standard route.

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 2: Scoping and handling release/cleanup of provided injection

Is it possible to scope this and provide a release method in the module or elsewhere to prevent memory leaks. Ex. I need to close a database connection in onDestroy() but it would be nice if this could be handled by the module itself.
Consider the following example* code.
*Read the error prone code at your own risk
Module
#dagger.Module
#lombok.NoArgsConstructor
public class PersistenceModule {
#Provides
#Singleton
DatabaseProvider providesDatabaseHelper(Context context) {
return new DatabaseProvider(context);
}
}
Activity
public class SomeActivity extends Activity{
#javax.inject.Inject DatabaseProvider provider;
//..onCreate omitted where injection happens.
#Override
protected void onDestroy() {
//Close database and cleanup.
provider.release();
provider = null;
super.onDestroy();
}
}
Your sample seems error-prone, as you are scoping your DatabaseProvider with a #Singleton scope, but use and clean it up in an activity.
A Module just helps creating objects—especially if there is no injectable constructor or it needs further initialization—and is not aware of further lifecycle events. It supplies its objects to a Component, which just holds and creates the object graph needed to inject your classes. In the end both are just plain old java objects and a scope on a component is nothing more than syntactic sugar helping with compile time validation.
In any case, you should handle your cleanup at the same scope that you provide your dependency. #Singleton scope should therefore be cleaned up in the application object that is also holding the application component. If you clean up a singleton scoped object in an activity, the next activity accessing it would be accessing an object in a closed state.
If every activity should have its own accessor and clean it up after being used, then you should switch to some activity based scope. Additional scopes are just annotations that you can create yourself.
The actual cleanup
All this said, I would not include "clean up" logic in my modules, because most people would not expect to find it there.
#Module
Annotates a class that contributes to the object graph.
Dagger is a dependency injection framework that provides dependencies for easier usage of interfaces and looser coupling of your classes. It is to reduce boiler plate code of object creation and what you do with the actual objects once you have them should not belong to the same code base creating them.
While it would still be possible to keep references to you modules, or make them implement some interface (still pojos!) and call them to clean up themselves in onDestroy it would probably lead to more confusion than just doing the cleanup where others would expect it.

Categories

Resources