使用带有挂起功能块的接口 类 的 Hilt 进行依赖注入
Dependency injection with Hilt of interface classes with suspend function blocks
我声明了一个使用挂起函数的 class。此 class 是 Hilt 库在 Android:
上注入的单例依赖项
interface Foo {
suspend fun send(v: Int)
suspend fun receive(): Int
}
class FooImpl @Inject () {
var channel: Channel<Int>(2, BufferOverflow.DROP_OLDEST)
override suspend fun send(v: Int) {
channel.send(v)
}
override suspend fun receive(): Int {
channel.receive()
}
}
//FIRST CASE:
@Module
@InstallIn(SingletonComponent::class)
abstract class FooModule {
@Binds
abstract fun bindFoo(foo: FooImpl): Foo
}
然后如果我调用接收函数时它永远被阻塞。没有收到数据,例子:
@AndroidEntryPoint
class Bar: Service {
@Inject
lateinit var foo: Foo
private val scope = CoroutineScope(Job() + Dispatchers.Default)
//...
fun doSomething() {
scope.launch() {
foo.receive()
//coroutine execution never reach this line
}
}
}
在这个简单的例子中,由于 Foo
是一个单例,我可以实施一个简单的解决方法。如果 Foo 在 Hilt 中以这种方式实现,我没有问题:
//SECOND_CASE:
val FOO: Foo = FooImpl()
@Module
@InstallIn(SingletonComponent::class)
object FooModule {
@Provides
fun providesFoo(): Foo {
return FOO
}
}
我想知道这是 Hilt 错误还是我的 FIRST_CASE hilt 模块实现有误?
您永远不会将 FooImpl
声明为单例,因此每次注入时,您都会得到一个新实例。
如果您认为这就是 @InstallIn(SingletonComponent::class)
所做的,那么事实并非如此。此注释仅告诉 hilt FooModule
本身应该是一个单例,并且不在 Activity、ViewModel 或 Fragment 的生命周期范围内。
您需要将 @Singleton
添加到 FooImpl
或绑定它的方法中:
选项 1
interface Foo {
suspend fun send(v: Int)
suspend fun receive(): Int
}
@Singleton
class FooImpl @Inject constructor() : Foo {
...
}
@Module
@InstallIn(SingletonComponent::class)
abstract class FooModule {
@Binds
abstract fun bindFoo(foo: FooImpl): Foo
}
选项 2
interface Foo {
suspend fun send(v: Int)
suspend fun receive(): Int
}
class FooImpl @Inject constructor() : Foo {
...
}
@Module
@InstallIn(SingletonComponent::class)
abstract class FooModule {
@Singleton
@Binds
abstract fun bindFoo(foo: FooImpl): Foo
}
我声明了一个使用挂起函数的 class。此 class 是 Hilt 库在 Android:
上注入的单例依赖项interface Foo {
suspend fun send(v: Int)
suspend fun receive(): Int
}
class FooImpl @Inject () {
var channel: Channel<Int>(2, BufferOverflow.DROP_OLDEST)
override suspend fun send(v: Int) {
channel.send(v)
}
override suspend fun receive(): Int {
channel.receive()
}
}
//FIRST CASE:
@Module
@InstallIn(SingletonComponent::class)
abstract class FooModule {
@Binds
abstract fun bindFoo(foo: FooImpl): Foo
}
然后如果我调用接收函数时它永远被阻塞。没有收到数据,例子:
@AndroidEntryPoint
class Bar: Service {
@Inject
lateinit var foo: Foo
private val scope = CoroutineScope(Job() + Dispatchers.Default)
//...
fun doSomething() {
scope.launch() {
foo.receive()
//coroutine execution never reach this line
}
}
}
在这个简单的例子中,由于 Foo
是一个单例,我可以实施一个简单的解决方法。如果 Foo 在 Hilt 中以这种方式实现,我没有问题:
//SECOND_CASE:
val FOO: Foo = FooImpl()
@Module
@InstallIn(SingletonComponent::class)
object FooModule {
@Provides
fun providesFoo(): Foo {
return FOO
}
}
我想知道这是 Hilt 错误还是我的 FIRST_CASE hilt 模块实现有误?
您永远不会将 FooImpl
声明为单例,因此每次注入时,您都会得到一个新实例。
如果您认为这就是 @InstallIn(SingletonComponent::class)
所做的,那么事实并非如此。此注释仅告诉 hilt FooModule
本身应该是一个单例,并且不在 Activity、ViewModel 或 Fragment 的生命周期范围内。
您需要将 @Singleton
添加到 FooImpl
或绑定它的方法中:
选项 1
interface Foo {
suspend fun send(v: Int)
suspend fun receive(): Int
}
@Singleton
class FooImpl @Inject constructor() : Foo {
...
}
@Module
@InstallIn(SingletonComponent::class)
abstract class FooModule {
@Binds
abstract fun bindFoo(foo: FooImpl): Foo
}
选项 2
interface Foo {
suspend fun send(v: Int)
suspend fun receive(): Int
}
class FooImpl @Inject constructor() : Foo {
...
}
@Module
@InstallIn(SingletonComponent::class)
abstract class FooModule {
@Singleton
@Binds
abstract fun bindFoo(foo: FooImpl): Foo
}