Tuesday, 9 July 2019

Submodules with Dagger and AndroidInjectors

I have been Dagger with the pre Android Injector style for a while, and have now decided to try the new methods. Til now, I was basically declaring on AppComponent like this:

@Singleton
@Component(
        modules = [ApplicationModule::class,
            NetModule::class,
            ApiModule::class,
            AnalyticsModule::class,
            DbModule::class,
            RepositoryModule::class,
            InteractorModule::class]
)
interface ApplicationComponent {
    fun inject(app: MyApp)
    fun plus(controllerModule: ControllerModule): ControllerComponent
}

Then I would inject my Activities/Fragments/Services/Dialogs like this:

class MyActivity : AppCompatActivity() {
...
    val component by lazy {
            (application as MyApp)
                    .applicationComponent
                    .plus(
                            ControllerModule(this)
                    )
        }

    override fun inject() {
            component.inject(this)
        }
...
}

Basically I had one top level app component with app-traversal modules, then an activity level component (ControllerComponent) with per-activity instances, common to all activities.

Now that I switched to the new methods, I create my component like so:

@Singleton
@Component(
        modules = [
            AndroidSupportInjectionModule::class,
            AppModule::class,
            NetModule::class,
            ApiModule::class,
            AnalyticsModule::class,
            DbModule::class,
            RepositoryModule::class,
            InteractorModule::class
        ]
)
interface AppComponent : AndroidInjector<SoulpicksApp> {

    @Component.Builder
    interface Builder {
        fun build(): AppComponent
        @BindsInstance
        fun application(application: SoulpicksApp): Builder
    }

}

Make my app extend DaggerApplication:

open class MyApp : DaggerApplication() {


    override fun applicationInjector(): AndroidInjector<out DaggerApplication> = DaggerAppComponent.builder().application(this).build()

}

And my Activities/Fragments extend DaggerAppCompatActivty/DaggerFragment respectively:

class MyActivity : DaggerAppCompatActivity() {

...
}

I understand this should automatically wire up all the activities dependencies, provided Dagger is properly set up. However I havent declared my ControllerModule/Component so of course when running my app I get:

e: /Users/user/dev/my-android/app/build/generated/source/kapt/devDebug/com/myapp/android/di/activity/ActivityBinder_ContributesMyActivity.java:28: error: @Subcomponent.Builder is missing setters for required modules or subcomponents: [com.myapp.android.di.controller.ControllerModule]

I understand previously I was creating this component on each Activity by using the plus() method and injecting explicitly (which is what I am trying to avoid here), how can I do that now?

Also, I have some BottomSheetDialogFragments and JobServiceIntents in my app, and theres no equivalent DaggerBottomSheedDialogFragments/DaggerJobServiceIntents to extend from, how can I work around that?

ControllerModule:

@Module
class ControllerModule(val activity: androidx.fragment.app.FragmentActivity) {

    @Provides
    @ControllerScope
    fun context(): Context = activity

    @Provides
    @ControllerScope
    fun activity() = activity

    @Provides
    @ControllerScope
    fun layoutInflater() = activity.layoutInflater

    @Provides
    @ControllerScope
    fun fragmentManager(): androidx.fragment.app.FragmentManager = activity.supportFragmentManager

    @Provides
    @ControllerScope
    fun provideNavigationController(activity: androidx.fragment.app.FragmentActivity, analyticsManager: AnalyticsCompositeManager) = NavigationController(activity, analyticsManager)

    @Provides
    @ControllerScope
    fun providePackageUtils(activity: androidx.fragment.app.FragmentActivity) : PackageUtils = PackageUtilsImpl(activity)
}



from Submodules with Dagger and AndroidInjectors

No comments:

Post a Comment