Dynamic Delivery is relatively new feature so it has a lot of limitations. One of those limitations it that you cannot access code and resources of a Dynamic Module in a conventional way, thus it cannot be a dependency for other modules. Currently you can access Dynamic Module via reflection and having dynamic features defined through public interfaces in a common library module and loading their actual implementations (located in the dynamic feature modules) at runtime with a ServiceLoader
. It has its performance downsides. They can be minimized with R8 using ServiceLoaderRewriter
but not completely removed.
While using reflection is very bug prone we can minimize it either with @AutoService
— AutoService
is an annotation processor that will scan the project for classes annotated with @AutoService
, for any class it finds it will automatically generate a service definition file for it.
Here is small example of how it is done
// All feature definitions extend this interface, T is the dependencies that the feature requires
interface Feature<T> {
fun getMainScreen(): Fragment
fun getLaunchIntent(context: Context): Intent
fun inject(dependencies: T)
}
interface VideoFeature : Feature<VideoFeature.Dependencies> {
interface Dependencies {
val okHttpClient: OkHttpClient
val context: Context
val handler: Handler
val backgroundDispatcher: CoroutineDispatcher
}
}
internal var videoComponent: VideoComponent? = null
private set
@AutoService(VideoFeature::class)
class VideoFeatureImpl : VideoFeature {
override fun getLaunchIntent(context: Context): Intent = Intent(context, VideoActivity::class.java)
override fun getMainScreen(): Fragment = createVideoFragment()
override fun inject(dependencies: VideoFeature.Dependencies) {
if (videoComponent != null) {
return
}
videoComponent = DaggerVideoComponent.factory()
.create(dependencies, this)
}
}
And to actually access code of Dynamic Feature use
inline fun <reified T : Feature<D>, D> FeatureManager.getFeature(
dependencies: D
): T? {
return if (isFeatureInstalled<T>()) {
val serviceIterator = ServiceLoader.load(
T::class.java,
T::class.java.classLoader
).iterator()
if (serviceIterator.hasNext()) {
val feature = serviceIterator.next()
feature.apply { inject(dependencies) }
} else {
null
}
} else {
null
}
}
Taken from here. Also there a lot more info there so I would recommend you to check it.
Generally I just would not recommend to use Dynamic Feature as dependency and plan your app architecture accordingly.
Hope it helps.