T
T
Tsuzukeru2021-05-10 17:27:42
Android
Tsuzukeru, 2021-05-10 17:27:42

How to build an organization of Dagger modules (AndroidInjector)?

I'm learning AndroidInjector by practicing on a simple app with App -> MainActivity -> MainFragment hierarchy.
Each of them has a module that provides a simple string. For example:

@Module
class AppModule {

    @Named("app string")
    @Provides
    fun provideAppString(): String {
        return "Dagger app string"
    }
}


The application works, but only with this component:
@Singleton
@Component(modules = [AndroidInjectionModule::class, ActivityProvider::class,FragmentProvider::class, AppModule::class, ActivityModule::class, FragmentModule::class])
interface AppComponent : AndroidInjector<DaggerApplication>


Activity and fragment providers are similar:
@Module
abstract class ActivityProvider {
    @ContributesAndroidInjector
    abstract fun mainActivity(): MainActivity
}


I want to put the ActivityModule and FragmentModule into their respective providers like this:

@Module
abstract class FragmentProvider {
    @ContributesAndroidInjector(modules = [FragmentModule::class])
    abstract fun mainFragment(): MainFragment
}


For a fragment, this works, but if you do the same for an activity, the application does not build. Something is wrong with inject in activity:
/home/x/GIT/DI/app/build/tmp/kapt3/stubs/debug/com/app/di/di/AppComponent.java:8: error: [Dagger/MissingBinding ] javax .inject.Named("activity string") java.lang.String cannot be provided without an @Provides-annotated method.
public abstract interface AppComponent extends dagger.android.AndroidInjector {
^
javax .inject.Named("activity string") java.lang.String is injected at
com.app.di.ui.fragment.MainFragment.activityString
com.app.di. ui.fragment.MainFragment is injected at
dagger.android.AndroidInjector.inject(T) [com.app.di.di.AppComponent → com.app.di.di.FragmentProvider_MainFragment.MainFragmentSubcomponent]

The complete code of the Activity and its components:

@Singleton
@Component(modules = [AndroidInjectionModule::class, ActivityProvider::class,FragmentProvider::class, AppModule::class])
interface AppComponent : AndroidInjector<DaggerApplication>


@Module
abstract class ActivityProvider {
    @ContributesAndroidInjector(modules = [ActivityModule::class])
    abstract fun mainActivity(): MainActivity
}


@Module
class ActivityModule {
    @Named("activity string")
    @Provides
    fun provideActivityString(): String {
        return "Dagger activity string"
    }
}


class MainActivity : DaggerAppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        if (savedInstanceState == null) {
            supportFragmentManager.beginTransaction()
                .add(R.id.fragment_container_view, MainFragment::class.java, null)
                .commit()
        }
    }

    companion object {
        private const val TAG = "MainActivity"
    }
}


class MainFragment : DaggerFragment(R.layout.fragment_main) {

    @Inject
    @Named("app string") 
    lateinit var appString: String
    @Inject
    @Named("activity string")
    lateinit var activityString: String
    @Inject
    @Named("fragment string")
    lateinit var fragmentString:String

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.i(TAG, "onCreate: $appString, $activityString, $fragmentString")
    }
    companion object {
        private const val TAG = "MainFragment"
    }
}

Answer the question

In order to leave comments, you need to log in

1 answer(s)
D
Denis Zagaevsky, 2021-05-10
@Tsuzukeru

I did not see where you declared that the generated fragment component will be a subcomponent for the activity component. If this is not the case (and it is not), then this string of yours is in another part of the graph, and, of course, you will not be able to access it.
I think that in ActivityProvider in ContributesAndroidInjector(modules) you should also specify FragmentProvider. Then the code will generate correctly.
And, accordingly, only AndroidInjectionModule, AppModule, and ActivityProvider should remain in the AppComponent.
Here you can read it https://m.habr.com/en/post/336462/

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question