[英]Hilt using in android library
我想在 android 库中试用 Hilt DI。
它依赖于另一个项目,有自己的子模块。 我遇到的第一个问题是需要用@HiltAndroidApp
标记Application
。 现在我的库中没有任何扩展Application
的东西,但我想利用 Hilt 及其预定义组件。
在这种情况下,是否有可能或者我应该使用 Dagger go? 我找到了 Dagger 的解决方案,其中库依赖注入是完全独立的(客户端不知道库的 DI): Dagger 解决方案,很想听听对此的任何意见,也许有人已经在这个问题上付出了很大的努力并可以分享他的见解。
如果您尝试将 Hilt 包含在 android库中,那么您应该期望 android应用程序(您的库的客户端)使用@HiltAndroidApp
标记其Application
。
您应该在库模块中包括您的整个设置(入口点、模块、依赖项……任何您想在库中拥有的内容),并要求库的客户端使用@HiltAndroidApp
来使用您的库正确。
您无需在库模块中包含@HiltAndroidApp
即可将库模块中的依赖项注入应用程序模块或任何动态功能模块。
此示例只有核心库模块、应用程序和动态功能模块。 动态功能模块实现是可选的。
从核心库模块注入 App 的 Activity 和 Fragment 的结果如下
Project dependency Structure
feature_hilt_camera feature_hilt_photos (Dynamic Feature Modules)
| | |
| ----App----
| |
core(android-library)
在core library module
中有一个匕首模块作为
@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()
}
并注入 Activity 作为
@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()}"
}
}
应用模块中的HomeFragment
也是如此
@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
}
如果您还希望注入动态功能模块,则需要在您的库模块中提供一个模块作为
/**
* 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
}
和动态功能模块,您将使用此接口作为依赖组件
在相机动态特征模块中有一个这样的组件
@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
}
}
并将其注入到您的动态特征片段中
private fun initCoreDependentInjection() {
val coreModuleDependencies = EntryPointAccessors.fromApplication(
requireActivity().applicationContext,
CoreModuleDependencies::class.java
)
DaggerCameraComponent.factory().create(
coreModuleDependencies,
requireActivity().application
)
.inject(this)
}
可以将 Hilt 集成到您的库中,但您必须处理应用不是 Hilt 应用的情况。 您可以通过使用@OptionalInject
注释您的活动/片段然后检查OptionalInjectCheck#wasInjectedByHilt()
来检查活动/片段是否由 Hilt 注入来处理这种情况。
@OptionalInject
@AndroidEntryPoint
public final class MyFragment extends Fragment {
...
@Override public void onAttach(Activity activity) {
super.onAttach(activity); // Injection will happen here, but only if the Activity used Hilt
if (!OptionalInjectCheck.wasInjectedByHilt(this)) {
// Get Dagger components the previous way and inject manually
}
}
}
请注意,这样做不会使您的库更简单(它实际上会变得更复杂,因为您需要同时支持 Hilt 和非 Hilt 应用程序)。 主要好处是对使用 Hilt 的客户而言,因为他们不需要进行任何组件/模块设置来让您的库在他们的应用程序中启动和运行。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.