kotlin 伴随对象中的Hilt注入

y1aodyip  于 2023-05-23  发布在  Kotlin
关注(0)|答案(1)|浏览(202)

我是Hilt/Dagger的新手,我还没有找到一个注入同伴对象的好例子。下面是我的单例和数据类。我尝试在create()函数中使用TargetNumber管理器。我正在寻找语法帮助和解释为什么。

@InstallIn(SingletonComponent::class)
@Module
data class TargetNumberManager(
val maxNumber: Int = 3, val prefix: String = "ZZ") : ITargetNumberManager {
private var currentNumber = 0

@Singleton
@Provides
override fun getNextTargetNumber(): String {
    val targetNumberBuilder: StringBuilder = java.lang.StringBuilder()

    targetNumberBuilder.append(prefix)

    val lengthOfNumber = currentNumber.toString().length

    for (i in lengthOfNumber..maxNumber step 1) {
        targetNumberBuilder.append(0)
    }

    targetNumberBuilder.append(currentNumber++)

    return targetNumberBuilder.toString()
}
}

//数据类

data class Target(
@PrimaryKey
val targetNumber: String,
val targetType: TargetType?,
val numOfElement: Int?,
val location: Coordinate?
) : java.io.Serializable {

@AndroidEntryPoint
companion object {
    fun create(): Target {
        @Inject var targetNumberManager : TargetNumberManager
        val nextNumber = targetNumberManager.getNextTargetNumber()
        return Target(nextNumber, null, null, null)
    }
}

}

pinkon5k

pinkon5k1#

看起来你想要的是让Dagger创建一个对象的示例。让我来解释一下如何使用EntryPoint s实现这一点。

我对你的匕首模块的备注

在您的类TargetNumberManager中:

  • 我会为Dagger模块使用普通的Kotlin类,避免使用接口或数据类。因为你不会创建你的模块的示例:匕首会创造它。
  • 避免在provides函数(如getNextTargetNumber)中使用变量,因为您提供的依赖项标记为@Singleton:这意味着Dagger只会调用你的getNextTargetNumber()函数1次。最好将业务逻辑(例如计入管理器、用例等)和依赖注入代码(例如将函数提供给Dagger模块)分开。

你可以像这样分离计数逻辑和Dagger模块:

@Singleton
data class TargetNumberManager @Inject constructor(
    val maxNumber: Int = 3,
    val prefix: String = "ZZ"
) : ITargetNumberManager {
    private var currentNumber = 0

    override fun getNextTargetNumber(): String {
        val targetNumberBuilder: StringBuilder = java.lang.StringBuilder()

        targetNumberBuilder.append(prefix)

        val lengthOfNumber = currentNumber.toString().length

        for (i in lengthOfNumber..maxNumber step 1) {
            targetNumberBuilder.append(0)
        }

        targetNumberBuilder.append(currentNumber++)

        return targetNumberBuilder.toString()
    }
}

请注意:

  • 我删除了@InstallIn(SingletonComponent::class)@Module注解。这意味着这个类不再是一个模块。因为TargetNumberManager看起来像一个业务逻辑的实现,它与依赖注入无关。
  • 我将@Inject注解添加到constructor。这意味着Dagger可以并且将会创建这个模块。为了让Dagger创建一个TargetNumberManager的示例,它需要找到TargetNumberManager的依赖关系,即maxNumberprefix变量。你也需要提供它们。

现在我们可以将它们提取为常量:

@Singleton
data class TargetNumberManager @Inject constructor() : ITargetNumberManager {
    private var currentNumber = 0

    override fun getNextTargetNumber(): String {
        val targetNumberBuilder: StringBuilder = java.lang.StringBuilder()

        targetNumberBuilder.append(PREFIX)

        val lengthOfNumber = currentNumber.toString().length

        for (i in lengthOfNumber..MAX_NUMBER step 1) {
            targetNumberBuilder.append(0)
        }

        targetNumberBuilder.append(currentNumber++)

        return targetNumberBuilder.toString()
    }
    
    companion object {
        private const val MAX_NUMBER: Int = 3
        private const val PREFIX: String = "ZZ"
    }
}

TargetNumberManager中有了这些变化,当你想在某个地方注入它时,Dagger可以创建它。它被标记为Singleton,所以Dagger将在您注入它时提供相同的示例。
Dagger需要知道当你尝试注入ITargetNumberManager的示例时,你想要使用哪个实现。因为你有一个接口和一个实现,但可能有其他实现的同一个接口,所以我们需要告诉Dagger使用哪一个。我们通过使用@Binds注解来做到这一点。@Binds方法必须是抽象的或在接口中。这里我使用了抽象类作为Dagger模块:

@InstallIn(SingletonComponent::class)
@Module
abstract class MyModule {

    @Binds
    abstract fun bindTargetNumberManager(impl: TargetNumberManager): ITargetNumberManager
}

现在Dagger知道当你询问ITargetNumberManager时注入哪个实现。请注意,我没有在这里放置@Singleton,因为实现类TargetNumberManager已经标记为@Singleton。因此,每当你注入类型TargetNumberManager AND ITargetNumberManager时,它将是Singleton。

如何通过入口点数获取匕首示例

  • 在plain Dagger中,您可以创建组件接口。Hilt包括了从普通匕首开始的所有东西,所以Hilt中也有组件。在Hilt中有EntryPoint接口。Hilt发现了这些接口,并使组件接口扩展了这些EntryPoint接口。使用EntryPoint基本上就像在组件接口中添加函数一样。参见:https://dagger.dev/hilt/entry-points
  • 您的组件接口和EntryPoint可以包含一个返回对象示例的函数。通过添加一个返回类型为您的依赖项的函数,您只需要求Dagger为您创建一个示例。

典型的EntryPoint如下所示:

@EntryPoint
@InstallIn(SingletonComponent::class)
interface MyEntryPoint {
    fun getTargetNumberManager(): ITargetNumberManager
}

当您需要ITargetNumberManager时:获取MyEntryPoint的示例,然后获取ITargetNumberManager的示例:

val myEntryPoint: MyEntryPoint = EntryPoints.get(applicationContext, MyEntryPoint::class.java)
val targetNumberManager: ITargetNumberManager = myEntryPoint.getTargetNumberManager()

如果你想在你的Target类中这样做:

data class Target(
    @PrimaryKey
    val targetNumber: String,
    val targetType: TargetType?,
    val numOfElement: Int?,
    val location: Coordinate?
) : java.io.Serializable {

    companion object {
        fun create(applicationContext: Context): Target {
            val myEntryPoint: MyEntryPoint = EntryPoints.get(applicationContext, MyEntryPoint::class.java)
            val targetNumberManager: ITargetNumberManager = myEntryPoint.getTargetNumberManager()
            val nextNumber = targetNumberManager.getNextTargetNumber()
            return Target(nextNumber, null, null, null)
        }
    }
}

Hilt需要知道Application Context,因为它将SingletonComponent保存在Application类中。所以它会去那里获取组件。获取组件后,它将强制转换为EntryPoint接口。然后,您可以调用添加到EntryPoint中的函数。

其他注意事项:

  • AndroidEntryPoint仅适用于Android入口点,例如:Activity、Fragment、View、Service、BroadcastReceiver。不适用于伴随对象或其他类型的类。参见:https://dagger.dev/hilt/android-entry-point.html
  • @Inject不能用于函数内部的变量。您可以将@Inject添加到构造函数中。

希望这能帮上忙。我试着解释每一点。如果您还有其他问题,请告诉我!

相关问题