Dagger2 includes dagger.android from 2.10, and 2.11 makes it even easier to use. Here, I will look at the generated code and write an explanation of what I am doing. I will also describe how to install using android support in Kotlin.
Gradle
build.gradle
buildscript {
ext.dagger_version = "2.12"
・ ・ ・
app/build.gradle
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
・ ・ ・
dependencies {
・ ・ ・
kapt "com.google.dagger:dagger-compiler:$dagger_version"
kapt "com.google.dagger:dagger-android-processor:$dagger_version"
implementation "com.google.dagger:dagger:$dagger_version"
implementation "com.google.dagger:dagger-android:$dagger_version"
・ ・ ・
Module
The following is how to write the Module added in Android Support.
AndroidModule.kt
@Module
abstract class AndroidModule {
@ContributesAndroidInjector
abstract fun contributeMainActivity(): MainActivity
}
@ContributesAndroidInjector is 2.It is an API made from 11.
This will generate the code you wrote in 2.10.
The auto-generated source will be explained later.
Component
#### **`AppComponent.kt`**
```kt
@Singleton
@Component(modules = arrayOf(AndroidInjectionModule::class, AndroidModule::class))
interface AppComponent {
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: App): Builder
fun build(): AppComponent
}
fun inject(app: App)
}
AndroidInjectionModule::class is miso. 2.Added from 10.
#### **`@BindsInstance is 2.Added from 9. When you build the component yourself, you can inject the argument into the component by calling the method that specifies it.`**
```Added from 9. When you build the component yourself, you can inject the argument into the component by calling the method that specifies it.
What this means is that if you don't write ``` @BindsInstance fun application (application: App): Builder```, for example, if you try to inject the Context as shown below, you will get a compile error.
@Module class AppModule { @Provides fun provideContext(application: App) = application.applicationContext }
class MainDao @Inject constructor() { @Inject lateinit var context: Context
#### **`@BindsInstance fun application(application: App):By describing the Builder and setting it in the Application class, it can be used in the DI state in the Module.`**
Application
Build the component using the class `` `DaggerAppComponent``` automatically generated from AppComponent.
App.kt
class App : Application(), HasActivityInjector {
@Inject lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
override fun activityInjector() = dispatchingAndroidInjector
override fun onCreate() {
super.onCreate()
val objectGraph = DaggerAppComponent
.builder()
.application(this)
.build()
objectGraph.inject(this)
}
}
The App class is made available in Module by using the method of @BindsInstance
specified in AppComponent. (This is the part of application (this) `` `.) Also, the `` `dagger.android.HasActivityInjector
interface is important. This is explained with the following Activity source.
Activity
Thanks to Android support, you can now just write ``` dagger.android.AndroidInjection.inject (this)` `` and it will be DI.
MainActivity.kt
lass MainActivity : AppCompatActivity() {
@Inject
lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
・ ・ ・
I will also post other classes.
MainViewModel.kt
class MainViewModel @Inject constructor() {
@Inject
lateinit var domain: MainDomain
・ ・ ・
dagger.android.AndroidInjection.inject(this)What is specifically doing is the Application class(App in this case.kt class)With dagger.android.If HasActivityInjector is implemented, dagger.android.Dispatching Using AndroidInjector Argument Activity(Here is MainActivity)Componet(This is a class for resolving dependencies. Also known as ObjectGraph.)I put it in. (@If you don't write ContributesAndroidInjector, you will get an assert error here. )
Then, it will inject into the field that is `` `@ Inject``` in Activity.
More specifically, `` `dagger.android.DispatchingAndroidInjector` `` is the code `` `MainActivitySubcomponentBuilder` `` automatically generated by `` `@ ContributesAndroidInjector` `` defined in Module with MainActivity as the key, and more. , Builder also instantiates the automatically generated class `` `MainActivitySubcomponentImpl``` (the implementation class of` `dagger.android.AndroidInjector```), and this class injects each class.
Below is a portion of the corresponding code described.
The following maybeInject method is located before AndroidInjection.inject (this) of Activity is called.
#### **`dagger.android.DispatchingAndroidInjector.java`**
public boolean maybeInject(T instance) { // ① instance=MainActivity Provider<AndroidInjector.Factory<? extends T>> factoryProvider = injectorFactories.get(instance.getClass()); if (factoryProvider == null) { return false; }
@SuppressWarnings("unchecked")
AndroidInjector.Factory<T> factory = (AndroidInjector.Factory<T>) factoryProvider.get(); // ② factory=MainActivitySubcomponentBuilder
try {
AndroidInjector<T> injector =
checkNotNull(
factory.create(instance), // ③ new MainActivitySubcomponentImpl()
"%s.create(I) should not return null.",
factory.getClass().getCanonicalName());
injector.inject(instance); // ④
The argument `` `T instance``` in line ① is MainActivity.
MainActivitySubcomponentBuilder is entered in ```AndroidInjector.Factory <T> factory``` in line ②.
MainActivitySubcomponentImpl is instantiated with ``` factory.create (instance)` `` in line ③.
In line ④, ```injector.inject (instance);` ``, the `` `MembersInjector <MainActivity>` `` held by the automatically generated `` `DaggerAppComponent``` (also automatically generated) `` `MainActivity_MembersInjector``` is the implementation class) is used to set the class to be DI in MainActivity.
# Automatically generated code
```@contributesandroidinjector```The code generated by is below.
In version 2.10 you had to write this yourself.
#### **`AndroidModule_ContributeMainActivity.java`**
```java
@Module(subcomponents = AndroidModule_ContributeMainActivity.MainActivitySubcomponent.class)
public abstract class AndroidModule_ContributeMainActivity {
private AndroidModule_ContributeMainActivity() {}
@Binds
@IntoMap
@ActivityKey(MainActivity.class)
abstract AndroidInjector.Factory<? extends Activity> bindAndroidInjectorFactory(
MainActivitySubcomponent.Builder builder);
@Subcomponent
public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MainActivity> {} // implemenets AndroidInjector.Factory
}
}
@binds
Is an annotation that saves you from writing the following standard code in a very simple way.
@Provides
AndroidInjector.Factory<? extends Activity> bindAndroidInjectorFactory(){
return new MainActivitySubcomponent.Builder()
}
Actually, `` `MainActivitySubcomponent.Builder``` is also abstract, so you have to implement the necessary methods, but that's what it is.
@With IntoMap@ActivityKey is used as a set. The one specified here is dagger.android.It will be a Map held by DispatchingAndroidInjector.(This Map will be important in the end)
#### **`@The value specified by ActivityKey (in this case, MainActivity).class) is the key. The value is the return value of the method (in this case MainActivitySubcomponent).Become a builder. By the way, this Builder is an Android Injector.It will be an implementation of Factory.`**
The following is the target part automatically generated from AppComponent.
DaggerAppComponent.java
・ ・ ・
private Provider<AndroidInjector.Factory<? extends Activity>> bindAndroidInjectorFactoryProvider;
・ ・ ・
@Override
public AppComponent build() {
if (application == null) {
throw new IllegalStateException(App.class.getCanonicalName() + " must be set");
}
return new DaggerAppComponent(this);
}
・ ・ ・
private DaggerAppComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.mainActivitySubcomponentBuilderProvider =
new dagger.internal.Factory<
AndroidModule_ContributeMainActivity.MainActivitySubcomponent.Builder>() {
@Override
public AndroidModule_ContributeMainActivity.MainActivitySubcomponent.Builder get() {
return new MainActivitySubcomponentBuilder();
}
}; //three
this.bindAndroidInjectorFactoryProvider = (Provider) mainActivitySubcomponentBuilderProvider; //Two
this.mapOfClassOfAndProviderOfFactoryOfProvider =
MapProviderFactory
.<Class<? extends Activity>, AndroidInjector.Factory<? extends Activity>>builder(1)
.put(MainActivity.class, bindAndroidInjectorFactoryProvider)
.build(); //Ichi bindAndroidInjectorFactoryProvider=dagger.internal.Factory()Anonymous class of implements Provider. Above 2,See, see
this.dispatchingAndroidInjectorProvider =
DispatchingAndroidInjector_Factory.create(mapOfClassOfAndProviderOfFactoryOfProvider);
this.appMembersInjector = App_MembersInjector.create(dispatchingAndroidInjectorProvider);
}
・ ・ ・
Let's take a concrete look at the "map of MainActivity.class is the key and the value is MainActivitySubcomponent.Builder" that I explained as important.
The bindAndroidInjectorFactoryProvider specified in the map value of Ichi is
Looking at the second, it becomes the mainActivitySubcomponentBuilderProvider.
In addition, mainActivitySubcomponentBuilderProvider is an anonymous class of dagger.internal.Factory
.
dagger.internal.Factory is javax.inject.The instance that inherits from Provider and is returned by get is the MainActivitySubcomponentBuilder class.
#### **`The parent class of MainActivitySubcomponentBuilder is MainActivitySubcomponent.Become a builder.`**
```Become a builder.
By the way, in Dagger, if the class that can be obtained by `` `javax.inject.Provider # get ()` `` can be injected, the Provider can be DI target.
In other words, the following definition is unnecessary.
@Provides
fun provideMainActivitySubcomponentBuilderProvider() = object: Provider
Therefore, the key is MainActivity and the value is `` `Provider <AndroidInjector.Factory <? Extends Activity >>` ``.
It is a continuation. It is a continuation of DaggerAppComponent.java.
#### **`DaggerAppComponent.java`**
```java
private final class MainActivitySubcomponentBuilder
extends AndroidModule_ContributeMainActivity.MainActivitySubcomponent.Builder {
private MainActivity seedInstance;
@Override
public AndroidModule_ContributeMainActivity.MainActivitySubcomponent build() {
if (seedInstance == null) {
throw new IllegalStateException(MainActivity.class.getCanonicalName() + " must be set");
}
return new MainActivitySubcomponentImpl(this); //"A"
}
@Override
public void seedInstance(MainActivity arg0) {
this.seedInstance = Preconditions.checkNotNull(arg0);
}
}
private final class MainActivitySubcomponentImpl
implements AndroidModule_ContributeMainActivity.MainActivitySubcomponent {
private MembersInjector<MainViewModel> mainViewModelMembersInjector;
private Provider<MainViewModel> mainViewModelProvider;
private MembersInjector<MainActivity> mainActivityMembersInjector;
private MainActivitySubcomponentImpl(MainActivitySubcomponentBuilder builder) {
assert builder != null;
initialize(builder);
}
@SuppressWarnings("unchecked")
private void initialize(final MainActivitySubcomponentBuilder builder) { //"I"
this.mainViewModelMembersInjector =
MainViewModel_MembersInjector.create(MainDomain_Factory.create());
this.mainViewModelProvider = MainViewModel_Factory.create(mainViewModelMembersInjector);
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(mainViewModelProvider);
}
@Override
public void inject(MainActivity arg0) {
mainActivityMembersInjector.injectMembers(arg0); //"U"
}
}
MainActivitySubcomponent.Builder instantiates MainActivitySubcomponentImpl with "A".
The actual instance of this is the part ③
factory.create (instance)`` explained in ``
dagger.android.DispatchingAndroidInjector.java, which is` It will be called from
AndroidInjection.inject (this) `, which is called at the beginning of` ʻActivity # onCreate ()
`.
Then, when `MainActivitySubcomponentImpl``` is instantiated, the method of" A "holds the Provider of the class you want to DI with ``` @ Inject``` ~ Injector class is automatically generated in the Factory class. Instance it and set it in the field that describes
`@ Inject``` of the passed Activity with "U".
The MainActivity_MembersInjector used in "U" is as follows.
MainActivity_MembersInjector.java
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
private final Provider<MainViewModel> viewModelProvider;
public MainActivity_MembersInjector(Provider<MainViewModel> viewModelProvider) {
assert viewModelProvider != null;
this.viewModelProvider = viewModelProvider;
}
public static MembersInjector<MainActivity> create(Provider<MainViewModel> viewModelProvider) {
return new MainActivity_MembersInjector(viewModelProvider);
}
@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.viewModel = viewModelProvider.get();
}
}
Above, the instance of viewModelProvider is below.
MainViewModel_Factory.java
public final class MainViewModel_Factory implements Factory<MainViewModel> {
private final MembersInjector<MainViewModel> mainViewModelMembersInjector;
public MainViewModel_Factory(MembersInjector<MainViewModel> mainViewModelMembersInjector) {
assert mainViewModelMembersInjector != null;
this.mainViewModelMembersInjector = mainViewModelMembersInjector;
}
@Override
public MainViewModel get() {
return MembersInjectors.injectMembers(mainViewModelMembersInjector, new MainViewModel());
}
public static Factory<MainViewModel> create(
MembersInjector<MainViewModel> mainViewModelMembersInjector) {
return new MainViewModel_Factory(mainViewModelMembersInjector);
}
}
@Singleton
The following will result in `` `@ Singleton```.
@Module
class AppModule {
@Singleton
@Provides
fun provideMainDomain() = MainDomain()
}
@Singleton
class MainDomain @Inject constructor()
@Singleton
@Component(modules = arrayOf(AndroidInjectionModule::class, AndroidModule::class, AppModule::class))
interface AppComponent
・ ・ ・
The code on the user side is as usual.
class MainViewModel @Inject constructor() {
@Inject
lateinit var domain: MainDomain
・ ・ ・
Let's see what happens when we do this
First, if you don't attach it.
DaggerAppComponent.java
・ ・ ・
private final class MainActivitySubcomponentImpl
implements AndroidModule_ContributeMainActivity.MainActivitySubcomponent {
・ ・ ・
private void initialize(final MainActivitySubcomponentBuilder builder) {
this.mainViewModelMembersInjector =
MainViewModel_MembersInjector.create(MainDomain_Factory.create()); //Here is different
・ ・ ・
The Provider class of MainDomain is as follows. This class also exists with @ Singleton
.
MainDomain_Factory.java
public final class MainDomain_Factory implements Factory<MainDomain> {
private static final MainDomain_Factory INSTANCE = new MainDomain_Factory();
@Override
public MainDomain get() {
return new MainDomain();
}
public static Factory<MainDomain> create() {
return INSTANCE;
}
}
mainviewmodel_membersinjector#create
The argument passed to is maindomain_In the factory, it gets()The method instantiates and returns the maindomain. factory=In the provider, it is instantiated every time get is called.
The following is the case with @ Singleton
.
mainviewmodel_membersinjector#create
The provider to pass to is different.
DaggerAppComponent.java
this.mainViewModelMembersInjector =
MainViewModel_MembersInjector.create(DaggerAppComponent.this.provideMainDomainProvider); //Here is different
public static AppComponent.Builder builder() {
return new Builder();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
・ ・ ・
this.provideMainDomainProvider =
DoubleCheck.provider(AppModule_ProvideMainDomainFactory.create(builder.appModule));
This `DoubleCheck.provider ()`
method holds the MainDomain for Singleton in the instance
field and returns it when the MainDomainProvider is called.
DoubleCheck.java
public final class DoubleCheck<T> implements Provider<T>, Lazy<T> {
private static final Object UNINITIALIZED = new Object();
private volatile Provider<T> provider;
private volatile Object instance = UNINITIALIZED;
private DoubleCheck(Provider<T> provider) {
assert provider != null;
this.provider = provider;
}
@Override
public T get() {
Object result = instance;
if (result == UNINITIALIZED) {
synchronized (this) {
result = instance;
if (result == UNINITIALIZED) {
result = provider.get(); // provider=AppModule_ProvideMainDomainFactory. MainDomain is instantiated here
Object currentInstance = instance;
if (currentInstance != UNINITIALIZED && currentInstance != result) {
throw new IllegalStateException("Scoped provider was invoked recursively returning "
+ "different results: " + currentInstance + " & " + result + ". This is likely "
+ "due to a circular dependency.");
}
instance = result; //Since MainDomain is cached here, the same instance is returned every time it is called.
provider = null;
}
}
}
return (T) result;
}
public static <T> Provider<T> provider(Provider<T> delegate) {
checkNotNull(delegate);
if (delegate instanceof DoubleCheck) {
return delegate;
}
return new DoubleCheck<T>(delegate); // App.Called by kt
}
By the way, the cached DoubleCheck instance is called from App.kt. For activities etc., you will get an instance from the cache using provideMainDomainProvider inside ```AndroidInjection.inject (this)` ``.
Recommended Posts