Dagger - Setting a Dynamic Value to SubComponent Builder [duplicate] - android

This question already has answers here:
Dagger 2.10 Android subcomponents and builders
(2 answers)
Closed 4 years ago.
I want to set a value to my sub-component builder at the time of building it. If simple Dagger 2 is concerned, we can achieve like following:
#UserScope
#Subcomponent(modules = {UserModule.class, ActivityBuilder.class, NewEditQuotationModule.class})
public interface UserComponent {
NewEditQuotationComponent.Builder getNewEditQuotationComponent();
void inject(UserManager userManager);
#Subcomponent.Builder
interface Builder {
#BindsInstance
Builder user(User user);
UserComponent build();
}}
But In case of Dagger Android, Subcomponent and its associated builder is handled by #ContributesAndroidInjector. Dagger Auto generate Subcomponent and its builder even its implementation with the current context.
I want to set some value at the time of building My Dagger Android Subcomponent. I tried by following approach:
#Subcomponent(modules = NewEditQuotationModule.class)
public interface NewEditQuotationComponent extends AndroidInjector<InquiriesEditNewMainActivity> {
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<InquiriesEditNewMainActivity> {
#BindsInstance
public abstract Builder setName(#Named("My_Name") String name);
}}
#Module
public abstract class NewEditQuotationModule {
#Binds
#IntoMap
#ActivityKey(InquiriesEditNewMainActivity.class)
abstract AndroidInjector.Factory<? extends Activity>
bindInquiriesEditNewMainActivityInjectorFactory(NewEditQuotationComponent.Builder builder);
}
I tired to build it by following way:
AndroidInjector.Builder androidInjector = MyApplication
.getApplication()
.getAppComponent()
.getApiEndPoint()
.getApiEndPointComponent()
.getUserManager()
.getUserComponent()
.getNewEditQuotationComponent().setName("My Name");
androidInjector.seedInstance(this);
androidInjector.build();
But not succeed.
Please let me know
How can I set some value at the time of building my component?
Where am I wrong in the previous approach?

dagger.android works by automatically creating your component in onCreate, and needs to work this way because Android is allowed to create your Activity on its own. Pre-creating your AndroidInjector will not work; that is what's wrong with your previous approach.
As I tried to indicate through a duplicate-question vote, the only way for you to set module instance or #BindsInstance values in dagger.android is by overriding the seedInstance method in the AndroidInjector.Builder that serves as your #Subcomponent.Builder as well. This will let you pull values out of the Activity instance and set them in your Activity graph.
Though you could always stash data in a #Singleton (ApplicationComponent-scoped) object or VM global variable, you are trying to do the right thing by passing data carefully from Activity to Activity without setting global state. I think this is the right approach, but also that your solution probably will not involve constructing an AndroidInjector explicitly. Instead, pass your data through the extras bundle, which is an idiomatic way of transferring parameters and other data into Activities; you can then insert that data into your graph by pulling it out of getIntent().getExtras() on your Activity instance in seedInstance, since getIntent() should be populated by the time onCreate(Bundle) runs.

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.

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.

Fragment injection without a dedicated subcomponent

I'm trying to use android-dagger to inject a fragment from a manually defined subcomponent:
#Component(modules = [
AndroidSupportInjectionModule::class,
AppModule::class,
BuilderModule::class
])
interface AppComponent : AndroidInjector<App> {
#Component.Builder
abstract class Builder : AndroidInjector.Builder<App>()
fun someComponent(): SomeComponent
}
#Subcomponent
interface SomeComponent {
fun inject(fragment: SomeFragment)
}
Execution fails with:
IllegalArgumentException: No injector factory bound for Class "SomeFragment"
However, if I create a fragment bind annotated with #ContributesAndroidInjector it executes fine. The doc states that all this does is create a subcomponent. Why can't I do that manually?
Minimal working project can be found on github:
https://github.com/absimas/fragment-injection
#ContributesAndroidInjector creates a subcomponent, yes. The docs don't say anything more, but they don't assert that this only creates a subcomponent; it also installs it into the Map<Class, AndroidInjector.Factory> multibinding that powers each of dagger.android's injection types.
You can see an example of this map binding on the Android page of the Dagger User's Guide:
#Binds
#IntoMap
#FragmentKey(YourFragment.class)
abstract AndroidInjector.Factory<? extends Fragment>
bindYourFragmentInjectorFactory(YourFragmentSubcomponent.Builder builder);
Note that this expects you to bind a Builder, not a Component: dagger.android expects that you'll want access to your Fragment instance from within your subcomponent, so the binding is for AndroidInjector.Factory<? extends Fragment> such that DispatchingAndroidInjector can call create(Fragment) on it. This is designed to be compatible with Subcomponent.Builder, but it does require that you define your #Subcomponent.Builder nested interface that extends the adapter AndroidInjector.Builder. Pardon my Java on a Kotlin question:
/** No need for an explicit inject, because you must extend AndroidInjector. */
#Subcomponent
interface SomeComponent extends AndroidInjector<SomeFragment> {
/**
* This abstract class handles the final create(Fragment) method,
* plus #BindsInstance.
*/
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<SomeFragment> {}
}
EDIT: It occurs to me now that your title states you don't want a dedicated subcomponent; beyond using a manual definition instead of #ContributesAndroidInjector, if you want a multi-Fragment subcomponent, then you might run into some trouble with this advice: dagger.android requires implementations of AndroidInjector<YourFragment>, and because of erasure, you own't be able to have a single class implement multiple AndroidInjector<T> or AndroidInjector.Builder<T> interfaces or abstract classes. In those cases you might need to write a manual AndroidInjector.Factory implementation which calls the correct concrete inject method. However, this seems like a lot of work for the sake of creating a monolithic Fragment component, when best-practices dictate dagger.android's default: a small and specific component for each Fragment.

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