Im investigating Dynamic Feature modules in my current Android Application.
My application consists of an App Module, a number of static modules and a single Dynamic Feature Module.
Im attempting to inject a repository class declared in the Dynamic Feature Module into a repository class in one of my static modules.
I have a common shared module that contains an interface that defines the API of
My dynamic feature module has a repository class that resembles this:-
class DynamicFeatureRepository @Inject constructor(private val applicationContext: Context) : MyExperimentable {
override fun accessDynamicModuleRawData(): List<MyExperimentDO> {
val myExperimentDOs = mutableListOf<MyExperimentDO>()
applicationContext.resources.openRawResource(R.raw.data).use {
val reader = BufferedReader(InputStreamReader(it))
while (reader.ready()) {
val columns = reader.readLine().split(",")
myExperimentDOs.add(
MyExperimentDO(
myExperimentId = columns[0].toLong(),
selected = columns[1].toBoolean(),
myExperimentName = columns[2],
myExperiment = columns[3]
)
)
}
}
return myExperimentDOs
}
override fun generate(data: String): Map<String, String> {
return emptyMap()
}
}
I have another static module that uses the above Dynamic Feature Module repository
I believe Dagger subcomponents will allow me to inject this Dynamic Feature Module repository into my static modules classes, however I cannot see how to achieve this.
So far I have the following Dagger classes declared in my common module:-
import dagger.Module
import dagger.Subcomponent
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Subcomponent
interface MyExperimentComponent {
fun inject(myExperimentable: MyExperimentable)
@Subcomponent.Factory
interface Factory {
fun create(): MyExperimentComponent
}
}
@InstallIn(SingletonComponent::class)
@Module(subcomponents = [MyExperimentComponent::class])
class SubcomponentsModule {}
In my Dynamic Feature Module I have this Dagger class:-
@InstallIn(SingletonComponent::class)
@Module
class ExperimentModule() {
@Provides
fun getMyExperiment(@ApplicationContext appContext: Context): MyExperimentable {
return DynamicFeatureRepository(appContext)
}
}
In my static module I have this @Inject
@Inject
lateinit var myExperimentable: MyExperimentable
My Application builds and runs, however the @Inject
in my static module is not being satisfied as my app fails with
kotlin.UninitializedPropertyAccessException: lateinit property myExperimentable has not been initialized
Where have I gone wrong?
How do I @Provide my dynamic feature module repo for one of my static modules
Below is the "Injection Site" within my static module:-
class MyRepository @Inject constructor(private val datastore: DataStore, private val workMonitor: WorkMonitor) {
@Inject
lateinit var myExperimentable: MyExperimentable
...
fun accessDynamicModuleRawData() {
val rawData = myExperimentable.accessDynamicModuleRawData()
Timber.i("xxx ${rawData.size}")
}
}
In the Android docs theres this Highlighted NOTE:-
Note: This issue happens whenever you want to create a subcomponent of ApplicationComponent. If you need to create a regular gradle module that depends on a feature module and needs to create a component that depends on a component defined in that feature module, you can use subcomponents as usual.
Isnt this the case I have? My static module needs/depends on a component defined within my Dynamic Feature Module. This comment makes me believe I can employ Dagger subcomponents to solve my problem. If this is true "How" do I use subcomponents to enable Injection of my DFM repo into my static module repo?
UPDATE
I have added this @EntryPoint
to my App module:-
@EntryPoint
@InstallIn(SingletonComponent::class)
interface MyExperimentableEntryPointInterface {
fun getMyExperimentable(): MyExperimentable
}
which will allow me to use this in my target Repository
val bar = EntryPoints.get(appContext, MyExperimentableEntryPointInterface::class.java).getMyExperimentable()
The problem I am encountering is how to "Provide
" the implementation of MyExperimentable from my DFM
In my DFM I have tried this:-
@Module
@DisableInstallInCheck
object DynamicFeatureRepositoryeModule {
@Provides
@Singleton
fun provideDynamicFeatureRepository(): MyExperimentable {
return DynamicFeatureRepository()
}
}
however I get this exception at build time:-
error: [Dagger/MissingBinding] MyExperimentable cannot be provided without an @Provides-annotated method.
What am I missing to be able to provide an instance of my DFM repository into my App?
UPDATED SOLUTION
I managed to achieve the desired result using Dagger and basing my solution on this sample The aspect I do not like though is this employs reflection