series

  • Dagger2 in Android (a) Popular basics
  • Dagger2 in Android (2) Advanced
  • Dagger2 in Android (iii) Scope with life cycle
  • Dagger2 in Android (四). Android extension library

The problem

As discussed earlier, the Dagger common structure on Android is to define a global AppComponent that other components rely on or inherit from. Suppose we have AppComponent and ActivityComponent. They might be written like this:

@Module(subcomponents = [ActivityComponent::class])
class AppModule(val context: Context) {

    @Provides
    @Singleton
    fun provideContext(a) = context
}

@Component(modules = [AppModule::class])
@Singleton
interface AppComponent {
	fun inject(app: MyApplication)
	
	fun activityComponent(a): ActivityComponent.Builder
}
Copy the code
@Module
class ActivityModule {
    @Provides
    fun provideSp(context: Context) =
            context.getSharedPreferences("Cooker", Context.MODE_PRIVATE)
}

@SubComponent(modules = [ActivityModule::class])
interface ActivityComponent {
	
	fun inject(activity: MainActivity)

	@Subcomponent.Builder
	interface Builder {
		fun build(a): ActivityComponent
	}
}
Copy the code

The two Components are defined above and they are containment relationships. Then we must instantiate the AppComponent in Appliction to ensure singletons:

class MyApplication: Application {
    lateinit var component: AppComponent
    override fun onCreate(a) {
        super.onCreate();
        component = DaggerAppComponent.builder().appModule(AppModule(this)).build(); }}Copy the code

Finally, if we want to use injection in our Activity, we’ll write:

class MainAty : AppCompatActivity() {
    @Inject
    lateinit var sp: SharedPreferences

    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
		(application as MyApplication).component
			.activityComponent()
			.build()
			.inject(this)}Copy the code

There are two problems with this approach:

  • Each Activity must create a Component instance. This code is repetitive and will be copied and pasted into many activities. This would be hard to refactor, and over time, no one even knew what the code actually was, becoming known as “family code.”
  • An Activity must know the type of Component it needs, which goes against the principle that the injected class should not care about the details of dependency injection.

To solve

This section is mainly derived from the Dagger official documentation.

Dagger. Android is a Dagger extension library to solve the above problem and make the use of Dagger on Android smoother. To use Dagger. Android, we first need to introduce, already discussed in the first section, the following three additional dependencies:

implementation "com.google.dagger:dagger-android:$dagger_version"
implementation "com.google.dagger:dagger-android-support:$dagger_version"
kapt "com.google.dagger:dagger-android-processor:$dagger_version"
Copy the code

Here’s an example of how injecting objects into MainActivity makes it easier to use the.Android extension library.

  1. You first need to install the extension library in the AppComponentAndroidInjectionModuleThis Module provides support for creating Android system components. For example, activities, fragments, etc.
@Component(modules = [AppModule::class, AndroidInjectionModule::class]) // Pay attention to this line
@Singleton
interface AppComponent {

    fun activityComponent(a): ActivityComponent.Builder
}
Copy the code
  1. Implement the Activity’s required child componentsAndroidInjector<Activity>Interface. And its Builder inheritsAndroidInjector.Factory. (Builder has been marked deprecated)
@SubComponent(modules = [ActivityModule::class])
interface ActivityComponent : AndroidInjector<MainActivity> {

	@Subcomponent.Factory
	interface Factory : AndroidInjector.Factory<MainActivity> {} // Notice that the build() method is no longer needed
}
Copy the code
  1. Create a new Module to bind the Subcomponent Factory and add it to the AppComponent.
@Module(subcomponents = [ActivityComponent::class])
abstract class ActivityBindModule {
    @Binds
    @IntoMap
    @ClassKey(MainAty::class)
    abstract fun bindMainAtyInjectorFactory(
		factory: ActivityComponent.Factory): AndroidInjector.Factory<*>
}

@Component(modules = [AppModule::class, AndroidInjectionModule::class, ActivityBindModule::class]) // Pay attention to this line
@Singleton
interface AppComponent {

    fun activityComponent(a): ActivityComponent.Builder
}
Copy the code

Tip:

If the child Component and its Factory (Builder) no other function, or inheritance in addition to the mentioned in step 2 classes, you can use annotations @ ContributesAndroidInjector to simplify the two step # 2, # 3.

Only need to add an abstract method in the Module, returns the corresponding Activity type, and then to annotate it with @ ContributesAndroidInjector, and specify the son to join the Component modules. If the child Component has Scope, apply the corresponding Scope annotation to the abstract method.

@Module(subcomponents = [LocalWatchFaceComponent::class])
abstract class ActivityBindModule {

    @ActivityScope
    @ContributesAndroidInjector(modules = [ActivityModule::class])
    abstract fun activityInjector(a): MainAty
}

// Don't forget to add AppComponent
Copy the code

Tip:

If multiple activities can be written to the same Bind abstract class, there is no need to write one separately.

  1. letApplicationClass implementsHasActivityInjectorInterface, and inject oneDispatchingAndroidInjector<Activity>Type dependent actionsactivityInjector()The return value of the function.
class MyApplication: Application.HasActivityInjector {
	@Inject
    lateinit var dispatchingActivityInjector: DispatchingAndroidInjector<Activity>
	
	override fun activityInjector(a) = dispatchingActivityInjector
	
    lateinit var component: AppComponent
    override fun onCreate(a) {
        super.onCreate();
        component = DaggerAppComponent.builder().appModule(AppModule(this)).build(); }}Copy the code
  1. Finally in the Activity’sonCreate()Method,super.onCreate()Before callingAndroidInjection.inject(this)Can.
class MainAty : AppCompatActivity() {
    @Inject
    lateinit var sp: SharedPreferences

    override fun onCreate(savedInstanceState: Bundle?). {
		AndroidInjection.inject(this) // <----
        super.onCreate(savedInstanceState)
}
Copy the code

Injection into the Activity is greatly simplified, and the Activity no longer needs to know the details of the Component.