Hilt using in android library

为君一笑 提交于 2020-12-03 08:51:33

问题


I would like to try Hilt DI in the android library.

It is a dependency on another project, with its own submodule. The very first problem I've encountered is the requirement of marking Application with @HiltAndroidApp. Now I do not have anything that extends Application in my library ofc but would like to utilize Hilt and its predefined components.

Is it possible or should I go with Dagger only in such a case? I've found a solution for Dagger, where library dependency injection is made totally independently (the client is unaware of the library's DI): Dagger solution, would love to hear any opinion on that, maybe someone already put a great effort into that issue and can share his insights.


回答1:


If you're trying to include Hilt in an android library, then you should expect the android app (client of your library) to mark its Application with @HiltAndroidApp.

You should include your whole setup (entry points, modules, dependencies, ... whatever you want to have in your library) in the library module, and make the requirement for the client of the library to use the @HiltAndroidApp to use your library correctly.




回答2:


You don't need to include @HiltAndroidApp in library module to inject the dependencies in library modules to app module or any dynamic feature modules.

This sample has only core library module, app, and dynamic feature modules. Dynamic feature module implementation is optional.

Result of injecting from core library module to App's Activity and Fragment is as

    Project dependency Structure
 feature_hilt_camera    feature_hilt_photos  (Dynamic Feature Modules)
        |         |          |
        |         ----App----
        |              |
        core(android-library)

In core library module have a dagger module as

@InstallIn(ApplicationComponent::class)
@Module
class CoreModule {

    @Singleton
    @Provides
    fun provideCoreDependency(application: Application) = CoreDependency(application)

    @Provides
    fun provideCoreActivityDependency(context: Application) = CoreActivityDependency(context)

    @Provides
    fun provideCoreCameraDependency(): CoreCameraDependency = CoreCameraDependency()

    @Provides
    fun provideCorePhotoDependency(): CorePhotoDependency = CorePhotoDependency()

    @Provides
    fun provideAnotherDependency() = AnotherDependency()
}

And inject to Activity as

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    /**
     * Injected from [CoreModule] with @Singleton scope
     */
    @Inject
    lateinit var coreDependency: CoreDependency

    /**
     * Injected from [CoreModule] with no scope
     */
    @Inject
    lateinit var coreActivityDependency: CoreActivityDependency

    /**
     * Injected from [MainActivityModule] with no scope
     */
    @Inject
    lateinit var toastMaker: ToastMaker

    /**
     *
     * Injected from [MainActivityModule] with @ActivityScoped
     * * To inject this there should be @Binds that gets Context from an Application
     */
    @Inject
    lateinit var mainActivityObject: MainActivityObject

    /**
     * Injected via constructor injection with no scope
     */
    @Inject
    lateinit var sensorController: SensorController

    /**
     * Injected via constructor injection with @Singleton scope
     *
     * ### Unlike Tutorial 9-2 This can be injected because MainActivity's component does not
     * depend on any component with another scope
     */
    @Inject
    lateinit var singletonObject: SingletonObject

    @Inject
    lateinit var anotherDependency: AnotherDependency

    @SuppressLint("SetTextI18n")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        findViewById<TextView>(R.id.tvInfo).text =
                "CoreModule @Singleton coreDependency: ${coreDependency.hashCode()}\n" +
                        "CoreModule no scope coreActivityDependency: ${coreActivityDependency.hashCode()}\n" +
                        "CoreModule no scope anotherDependency: ${anotherDependency.hashCode()}\n" +
                        "MainActivityModule @ActivityScoped mainActivityObject: ${mainActivityObject.hashCode()}\n" +
                        "MainActivityModule no scope toastMaker: ${toastMaker.hashCode()}\n" +
                        "Constructor no scope sensorController: ${sensorController.hashCode()}\n"
        "Constructor @Singleton singletonObject: ${singletonObject.hashCode()}"


    }
}

and it's same for HomeFragment which is in app module

@AndroidEntryPoint
class HomeFragment : Fragment() {


    /**
     * Injected from [CoreModule] with @Singleton scope
     */
    @Inject
    lateinit var coreDependency: CoreDependency

    /**
     * Injected from [CoreModule] with no scope
     */
    @Inject
    lateinit var coreActivityDependency: CoreActivityDependency

    @Inject
    lateinit var homeFragmentObject: HomeFragmentObject

    /**
     * This dependency cannot be injected since this fragment's component does not depend on CoreComponent
     * unlike Tutorial 9-2 counterpart
     */
    @Inject
    lateinit var mainActivityObject: MainActivityObject

    @Inject
    lateinit var fragmentObject: FragmentObject
}

If you also wish to inject to dynamic feature modules you need a provision module in your library module as

/**
 * This component is required for adding component to DFM dependencies
 */
@EntryPoint
@InstallIn(ApplicationComponent::class)
interface CoreModuleDependencies {

    /*
        🔥 Provision methods to provide dependencies to components that depend on this component
     */
    fun coreDependency(): CoreDependency

    fun coreActivityDependency(): CoreActivityDependency

    fun coreCameraDependency(): CoreCameraDependency

    fun corePhotoDependency(): CorePhotoDependency

}

and dynamic feature module you will use this interface as dependent component

In camera dynamic feature module have a component like this

@Component(
        dependencies = [CoreModuleDependencies::class],
        modules = [CameraModule::class]
)
interface CameraComponent {

    fun inject(cameraFragment1: CameraFragment1)
    fun inject(cameraFragment2: CameraFragment2)


    fun inject(cameraActivity: CameraActivity)

    @Component.Factory
    interface Factory {
        fun create(coreComponentDependencies: CoreModuleDependencies,
                   @BindsInstance application: Application): CameraComponent
    }

}

and inject it to your dynamic feature fragment with

private fun initCoreDependentInjection() {

    val coreModuleDependencies = EntryPointAccessors.fromApplication(
            requireActivity().applicationContext,
            CoreModuleDependencies::class.java
    )

    DaggerCameraComponent.factory().create(
            coreModuleDependencies,
            requireActivity().application
    )
            .inject(this)
}

Full sample that in image is here, and you check out implementation for both libraries and dynamic feature modules in this sample project.



来源:https://stackoverflow.com/questions/63246625/hilt-using-in-android-library

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!