How to use Android Injector for Activity and Fragment objects through New Dagger 2 (with Kotlin)

2020-11-22 21:24:01

参考地址 参考文章 https://medium.com/@serapbercin001/how-to-use-android-injector-for-activity-and-fragment-objects-through-new-dagger-2-with-kotlin-704eb8a98c43



This article is regarding how to implement dagger 2 with new approach, It brought giant change and in this way getting ride of boilerplate code through dagger 2.x new release. However I don’t compare between old and new dagger structure so I just have developed a simple sample from scratch about that and basically touch on @Component@Module@ContributesAndroidInjectorAndroidInjection.inject(this)AndroidSupportInjection.inject(this).

If you want to use these, first of all we should set up gradle:

Set up gradle:

kapt 'com.google.dagger:dagger-compiler:2.16'
kapt 'com.google.dagger:dagger-android-processor:2.16'
implementation 'com.google.dagger:dagger-android-support:2.16'

i added kapt instead of annotationProcessor for kotlin. we dont need to keep annotationProcessor dependency. Also we have to add kotlin-plugin in gradle.

apply plugin: 'kotlin-kapt'

Basically Package Structure:


Let’s start to create ApplicationComponent interface that is root in our dagger graph. It may includes more modules that just listed here. For instance; api module, shared preference module and something like that. I started to add AndroidInjectionModule as a module in the component. This module should be installed in the component that is used to inject. AndroidInjectionModule also is internal module, it was created by Dagger, it includes binding activity, fragment, service, broadcastReciver and content provider. We will add ActivitiesModule module later on.

@Singleton
@Component(modules = AndroidInjectionModule::class)
interface ApplicationComponent {

  fun inject(application: Application)

  @Component.Builder
  interface Builder {

     fun build(): ApplicationComponent

     @BindsInstance
     fun applicationBind(application: Application): Builder

  }
}

Let’s start to write ActivitiesModule as a subcomponent, we will use @ContributesAndroidInjector annotation type. After that we will add it in ApplicationComponent as a module.

@Module
abstract class ActivitiesModule {
  @ContributesAndroidInjector(modules =    [(MainActivityModule::class)])
  abstract fun contributeActivityAndroidInjector(): MainActivity

}

@ContributesAndroidInjector can takes modules, Modules to be installed in the generated. I’ll explain the MainActiviyModule that what should include, also with @Bind annotation . By the way If module needs scope, we can create spesific Scope, for instance; @ActivityScope, @FragmentScope..

import javax.inject.Scope

@Scope
@MustBeDocumented
annotation class ActivityScope

if you need it, we can add scope like here.

@ActivityScope
@ContributesAndroidInjector(modules = [(ActivityModule::class)])
abstract fun contributeActivityAndroidInjector(): MainActivity

Dagger 2 includes HasActivityInjector as a new that provides AndroidInjector and should implement in Application class, also we should inject DispatchingAndroidInjector that’s mean return activityInjector, in this way we’ll use AndroidInjection.inject() for activity and fragment.

class SampleApplication : Application(), HasActivityInjector {

   @Inject
   lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>


   override fun onCreate() {
       super.onCreate()
       DaggerApplicationComponent.builder()
               .applicationBind(this)
               .build()
               .inject(this)

   }

   override fun activityInjector(): AndroidInjector<Activity> = dispatchingAndroidInjector

}

We can use AndroidInjection.inject(this) in activity after inject DispatchingAndroidInjector in application.

AndroidInjection.inject(this) should calls in onCreate method before super. That’s all!!

class MainActivity : AppCompatActivity(){ override fun onCreate(savedInstanceState: Bundle?) {
  AndroidInjection.inject(this)
  super.onCreate(savedInstanceState)
  setContentView(R.layout.activity_main)
}
}

you can see generated file below that code was created by dagger after rebuild. Remember, via androidInjector.inject(this) we passed activity, it gets DispatchingAndroidInjector<Activity> from application.

private DispatchingAndroidInjector<Activity> getDispatchingAndroidInjectorOfActivity() {
 return DispatchingAndroidInjector_Factory.newDispatchingAndroidInjector(
     getMapOfClassOfAndProviderOfFactoryOf());
}

DispatchingAndroidInjector looks up AndroidInjector.Factory, it is below the code snippet. it creates your activities subcomponent builder, and pass the activities to inject your activities.

private Map<Class<? extends Activity>, Provider<AndroidInjector.Factory<? extends Activity>>>
   getMapOfClassOfAndProviderOfFactoryOf() {
 return MapBuilder
     .<Class<? extends Activity>, Provider<AndroidInjector.Factory<? extends Activity>>>
         newMapBuilder(2)
     .put(MainActivity.class, (Provider) mainActivitySubcomponentBuilderProvider)
     .put(SecondActivity.class, (Provider) secondActivitySubcomponentBuilderProvider)
     .build();
}

Inject Fragment Object

So far, We injected Activity object.Well, How we can inject fragment object. We will modify activity class to inject fragment object and implement HasSupportFragmentInjector or HasFragmentInjector interface. HasSupportFragmentInjector is for android.support.v4.app.Fragment and HasFragmentInjector is for android.app.Fragment after that we pass fragment in DispatchingAndroidInjector, that’s looks here.

class MainActivity : AppCompatActivity(), HasSupportFragmentInjector {

  @Inject
  lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>

  override fun onCreate(savedInstanceState: Bundle?) {
     AndroidInjection.inject(this)
     super.onCreate(savedInstanceState)
     setContentView(R.layout.activity_main)
  }

  override fun supportFragmentInjector() = dispatchingAndroidInjector

}

Also, we create Fragment module same way with the activity.

@Module
abstract class FragmentModule {

  @ContributesAndroidInjector
  abstract fun contributeMainFragment(): MainFragment
}

If you need to add MainFragmentModule, simply add like this;

@ContributesAndroidInjector(modules = [(MainFragmentModule::class)])

we should specify FragmentModule in our root graph after created fragment module

@Component(modules = [AndroidSupportInjectionModule::class,
  ActivitiesModule::class,
  FragmentModule::class])

now we can inject fragment object. For fragment we can call AndroidSupportInjection.inject(this) in onAttach method before super.

class MainFragment : Fragment() {

  override fun onAttach(context: Context?) {
     AndroidSupportInjection.inject(this)
     super.onAttach(context)

  }

  override
  fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                   savedInstanceState: Bundle?): View? {
     return inflater.inflate(R.layout.main_fragment, container, false)
  }

}

That’s all:) You also can find source code GitHub.


  • 2017-01-16 15:09:40

    Javascript模块化编程(一):模块的写法

    随着网站逐渐变成"互联网应用程序",嵌入网页的Javascript代码越来越庞大,越来越复杂。网页越来越像桌面程序,需要一个团队分工协作、进度管理、单元测试等等......开发者不得不使用软件工程的方法,管理网页的业务逻辑。

  • 2017-01-16 15:16:24

    Javascript模块化编程(二):AMD规范

    AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。

  • 2017-01-16 15:19:24

    RequireJS 入门指南

    如今最常用的JavaScript库之一是RequireJS。最近我参与的每个项目,都用到了RequireJS,或者是我向它们推荐了增加RequireJS。在这篇文章中,我将描述RequireJS是什么,以及它的一些基础场景。

  • 2017-01-16 15:22:30

    CommonJS规范

    CommonJS模块规范。 根据这个规范,每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。

  • 2017-01-17 15:42:03

    vue-cli 发布(译)

    当我们真正开发一个应用的时候,我们不可避免的会用到一大堆的工具,模块化、预处理器、热模块加载、代码校验和测试。这些工具对于一个需要长期维护的大型应用是必须的,但是项目初始化将会是让人痛苦的事情。这就是为什么我们做了 vue-cli 。