dagger.android多模塊項目實現(一)

本文適合有必定的Dagger2使用基礎的同窗java

上一篇:Dagger2多模塊項目Component組織方式選擇(二)
下一篇:dagger.android多模塊項目實現(二)android

前兩篇文章咱們講了兩種多模塊項目怎麼使用Dagger2。
發如今每一個Activity的onCreate中都須要調一個inject方法git

NewsComponentHolder.newsComponent.inject(this)
UserComponentHolder.userComponent.inject(this)

其實還能夠用dagger2專給android使用的dagger.android來簡化這種操做。 github

先看普通多模塊項目segmentfault

咱們在Dagger2多模塊項目Component組織方式選擇(一)的基礎上改造實現app

dagger.android的核心思想是在每一個Component收集兩個Mapide

Map<Class<?>, Provider<AndroidInjector.Factory<?>>> injectorFactoriesWithClassKeys
Map<String, Provider<AndroidInjector.Factory<?>>> injectorFactoriesWithStringKeys

這兩個Map定義在AndroidInjectionModule中this

@Beta
@Module
public abstract class AndroidInjectionModule {
  @Multibinds
  abstract Map<Class<?>, AndroidInjector.Factory<?>> classKeyedInjectorFactories();

  @Multibinds
  abstract Map<String, AndroidInjector.Factory<?>> stringKeyedInjectorFactories();

  private AndroidInjectionModule() {}
}

dagger.android會把把收集到的這兩個Map注入到DispatchingAndroidInjector中,dagger.android就是經過這個DispatchingAndroidInjector注入到Activity,Fragment中spa

怎麼收集呢code

首先定義一個 xxxBindModule ,將要注入的Activity,fragment用@ContributesAndroidInjector註解

dagger.android會把這些收集到前面的Map中去

@Module(includes = [AndroidInjectionModule::class])
abstract class NewsBindModule {
    @ContributesAndroidInjector
    abstract fun newsActivity(): NewsActivity
}

而後相應的Component的modules加上xxxBindModule,

去掉inject(XXXActivity)這樣的一大堆聲明方法,乾淨多了

@NewsScope
@Subcomponent(modules = [NewsModule::class, NewsBindModule::class])
interface NewsComponent {

    @Subcomponent.Factory
    interface Factory {
        fun create(): NewsComponent
    }
}

以後按照dagger.android用法要讓Application實現HasAndroidInjector接口,並注入dispatchingAndroidInjector實例

class AppApplication : BaseApplication(), NewsComponentProvider, UserComponentProvider, HasAndroidInjector {
      
      @Inject
    lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
      
    lateinit var appComponent: AppComponent
    override fun onCreate() {
        super.onCreate()
        appComponent = DaggerAppComponent.factory().create(this)
    }

    override fun provideNewsComponent(): NewsComponent {
        return appComponent.newsComponentFactory().create()
    }

    override fun provideUserComponent(): UserComponent {
        return appComponent.userComponentFactory().create()
    }
  
    override fun androidInjector(): AndroidInjector<Any> {
        return dispatchingAndroidInjector
    }
}

再在Component加上一個注入到上面Appliction的方法(由於news模塊拿不到AppApplication的引用,直接注入到Any好了)

@NewsScope
@Subcomponent(modules = [NewsModule::class, NewsBindModule::class])
interface NewsComponent {

    @Subcomponent.Factory
    interface Factory {
        fun create(): NewsComponent
    }
  
    fun inject(any: Any)
}

而後在AppApplication中注入

class AppApplication : BaseApplication(), NewsComponentProvider, UserComponentProvider {
      
      @Inject
    lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
      
    lateinit var appComponent: AppComponent
    override fun onCreate() {
        super.onCreate()
        appComponent = DaggerAppComponent.factory().create(this)
          NewsComponentHolder.newsComponent.inject(this)
    }

    override fun provideNewsComponent(): NewsComponent {
        return appComponent.newsComponentFactory().create()
    }

    override fun provideUserComponent(): UserComponent {
        return appComponent.userComponentFactory().create()
    }
  
    override fun androidInjector(): AndroidInjector<Any> {
        return dispatchingAndroidInjector
    }
}

最後在Activity,fragment的onCreate方法中加入AndroidInjection.inject(this),注意要放在super.onCreate(savedInstanceState)前面,咱們把這一步放在BaseActivity,BaseFragment裏

open class BaseActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        AndroidInjection.inject(this)
        super.onCreate(savedInstanceState)
    }
}

而後Activity只要繼承BaseActivity就能夠了,不須要寫任何注入代碼了,像平時使用同樣了,想要注入對象的變量加 @Inject就能夠了

class NewsActivity : BaseActivity() {

    @Inject
    lateinit var set: Set<String>

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_news)
        text.text = set.toString()
    }
}

這種寫法對於單模塊項目沒有問題,可是對多模塊項目來講這有問題了,上面咱們只注入了news模塊的,user模塊的沒有。咱們有多個Component,可是這裏只有一個dispatchingAndroidInjector,你用哪一個Component注入都不全,後面注入的會覆蓋前面注入的。因此這裏要改造下

從前面咱們知道一個Component最終生成一個DispatchingAndroidInjector,多個Component咱們把它們都收集起來

咱們先定義一個BaseDispatchingInjector,它至關於前面的AppApplication,接收一個Component注入的DispatchingAndroidInjector

class BaseDispatchingInjector  {
    @Inject
    lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
}

而後把每一個Component裏的inject(any: Any)改爲inject(baseDispatchingInjector: BaseDispatchingInjector)

@NewsScope
@Subcomponent(modules = [NewsModule::class, NewsBindModule::class])
interface NewsComponent {

    @Subcomponent.Factory
    interface Factory {
        fun create(): NewsComponent
    }

    fun inject(baseDispatchingInjector: BaseDispatchingInjector)
}
@UserScope
@Subcomponent(modules = [UserModule::class, UserBindModule::class])
interface UserComponent {
    @Subcomponent.Factory
    interface Factory {
        fun create(): UserComponent
    }

    fun inject(baseDispatchingInjector: BaseDispatchingInjector)
}

這樣注入

val userDispatchingInjector = BaseDispatchingInjector()
UserComponentHolder.userComponent.inject(userDispatchingInjector)
val newsDispatchingInjector = BaseDispatchingInjector()
NewsComponentHolder.newsComponent.inject(newsDispatchingInjector)

這樣咱們每一個模塊都獲得一個BaseDispatchingInjector,而且裏面每一個Activity,Fragment對應的Map都注入好了

而後就要定義一個MultiModuleAndroidInjector把每一個模塊的BaseDispatchingInjector整合到一塊兒成爲一個單獨的AndroidInjector

class MultiModuleAndroidInjector : AndroidInjector<Any> {

    private val injectors = mutableListOf<BaseDispatchingInjector>()

    fun addInjector(injector: HasDispatchingInjector) {
        injectors.add(injector)
    }

    override fun inject(instance: Any) {
        val wasInjected = injectors.any { it.dispatchingAndroidInjector.maybeInject(instance) }
        if (!wasInjected) {
            throw IllegalArgumentException("injection failed")
        }
    }
}

這個MultiModuleAndroidInjector在注入的時候會每一個BaseDispatchingInjector都去嘗試看能不能注入,這樣就把全部Component的註解都遍歷了

看AppApplication最後實現

class AppApplication : BaseApplication(), NewsComponentProvider, UserComponentProvider,
    HasAndroidInjector {

    lateinit var appComponent: AppComponent

    private val multiModuleAndroidInjector = MultiModuleAndroidInjector()

    override fun onCreate() {
        super.onCreate()
        appComponent = DaggerAppComponent.factory().create(this)
        val userDispatchingInjector = BaseDispatchingInjector()
        UserComponentHolder.userComponent.inject(userDispatchingInjector)
        multiModuleAndroidInjector.addInjector(userDispatchingInjector)
        val newsDispatchingInjector = BaseDispatchingInjector()
        NewsComponentHolder.newsComponent.inject(newsDispatchingInjector)
        multiModuleAndroidInjector.addInjector(newsDispatchingInjector)
    }

    override fun provideNewsComponent(): NewsComponent {
        return appComponent.newsComponentFactory().create()
    }

    override fun provideUserComponent(): UserComponent {
        return appComponent.userComponentFactory().create()
    }

    override fun androidInjector(): AndroidInjector<Any> {
        return multiModuleAndroidInjector
    }
}

代碼地址

相關文章
相關標籤/搜索