First, you should think over Vasily's answer.
But let's think for a moment how we did this before Dagger Android?
We built a subcomponent from the component that was taken from the Application
class. Later, we could use this subcomponent in order to inject fields, for example, of a custom view.
So, we'll try to do the exact same thing now.
Suppose, our aim is to inject MyAdapter
class into a MyButton
:
public class MyButton extends AppCompatButton {
@Inject MyAdapter adapter;
public MyButton(Context context) {
super(context);
...
}
}
And let's make the adapter have a dependency on the activity Context
, not application Context
:
public class MyAdapter {
@Inject
public MyAdapter(@Named("activity") Context context) {
}
}
Let's start with the custom Application
class.
MyApplication.java
public class MyApplication extends DaggerApplication {
@Inject
DispatchingAndroidInjector<Activity> dispatchingActivityInjector;
public static MySubcomponent mySubcomponent;
@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return DaggerAppComponent.builder()
.create(this);
}
}
AppComponent.java:
@Component(modules = {AndroidSupportInjectionModule.class, ActivityBindingModule.class, AppModule.class})
@Singleton
public interface AppComponent extends AndroidInjector<MyApplication> {
@Component.Builder
abstract class Builder extends AndroidInjector.Builder<MyApplication> {
}
}
AppModule.java
@Module
abstract class AppModule {
@Binds
@Singleton
@Named("app")
abstract Context providesContext(Application application);
}
ActivityBindingModule.java
@Module(subcomponents = MySubcomponent.class)
public abstract class ActivityBindingModule {
@Binds
@IntoMap
@ActivityKey(MainActivity.class)
abstract AndroidInjector.Factory<? extends Activity>
bindMainActivityInjectorFactory(MySubcomponent.Builder builder);
}
AndroidSupportInjectionModule.java
is shipping with dagger itself. If you do not use classes from support package (i.e. android.support.v4.app.Fragment
instead of android.app.Fragment
), then use AndroidInjectionModule.java
.
MySubcomponent.java
@ActivityScope
@Subcomponent(modules = {SubcomponentModule.class/*, other modules here, if needed */})
public interface MySubcomponent extends AndroidInjector<MainActivity> {
void inject(MyButton button);
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MainActivity> {
public abstract MySubcomponent build();
}
}
SubcomponentModule.java
@Module
abstract class SubcomponentModule {
@Binds
@ActivityScope
@Named("activity")
abstract Context toContext(MainActivity activity);
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Inject
MySubcomponent subcomponent;
@Override
protected void onCreate(Bundle savedInstanceState) {
// Will inject `subcomponent` field
AndroidInjection.inject(this);
// Saving this component in a static field
// Hereafter you are taking responsibility of mySubcomponent lifetime
MyApplication.mySubcomponent = subcomponent;
super.onCreate(savedInstanceState);
setContentView(new MyButton(this));
}
}
Having all of these, now here's how MyButton
will look like:
public class MyButton extends AppCompatButton {
@Inject MyAdapter adapter;
public MyButton(Context context) {
super(context);
MyApplication.mySubcomponent.inject(this);
}
}
I admit that this looks hacky and certainly not an approach to stick to. I'm happy to see a better approach.