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-06-18 02:34:22

    谈mysql中utf8和utf8mb4区别

    MySQL在5.5.3之后增加了这个utf8mb4的编码,mb4就是most bytes 4的意思,专门用来兼容四字节的unicode。好在utf8mb4是utf8的超集,除了将编码改为utf8mb4外不需要做其他转换。当然,为了节省空间,一般情况下使用utf8也就够了。

  • 2017-07-05 09:48:51

    CSS 元素垂直居中的 6种方法

    利用CSS进行元素的水平居中,比较简单,行级元素设置其父元素的text-align center,块级元素设置其本身的left 和 right margins为auto即可。本文收集了六种利用css进行元素的垂直居中的方法,每一种适用于不同的情况,在实际的使用过程中选择某一种方法即可。

  • 2017-07-06 10:02:18

    大白话讲解Promise(一)

    去年6月份, ES2015正式发布(也就是ES6,ES6是它的乳名),其中Promise被列为正式规范。作为ES6中最重要的特性之一,我们有必要掌握并理解透彻。本文将由浅到深,讲解Promise的基本概念与使用方法。

  • 2017-07-11 21:54:14

    MYSQL5.7版本sql_mode=only_full_group_by问题

    一旦开启 only_full_group_by ,感觉,group by 将变成和 distinct 一样,只能获取受到其影响的字段信息,无法和其他未受其影响的字段共存,这样,group by 的功能将变得十分狭窄了

  • 2017-07-14 13:51:58

    NodeJS连接MySQL出现Cannot enqueue Handshake after invoking quit.

    原因在于node连接上mysql后如果因网络原因丢失连接或者用户手工关闭连接后,原有的连接挂掉,需要重新连接;如下代码,每次访问结束都关闭,每次开始访问前重连接下,代码中没有监听连接的fatal错误,copy需谨慎

  • 2017-07-14 13:53:02

    nodejs解决mysql和连接池(pool)自动断开问题

    最近在做一个个人项目,数据库尝试使用了mongodb、sqlite和mysql。分享一下关于mysql的连接池用法。项目部署于appfog,项目中我使用连接池链接数据库,本地测试一切正常。上线以后,经过几次请求两个数据接口总是报503。一直不明就里,今天经过一番排查终于顺利解决了。