伴随对象中的 Hilt 注入

Ker*_*rry 10 android kotlin dagger-hilt

我是 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()
    }
}
Run Code Online (Sandbox Code Playgroud)

// 数据类

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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Mus*_*tlu 3

看起来你想要的是要求 Dagger 创建一个对象的实例。让我解释一下如何使用EntryPoints 做到这一点。

我对 Dagger 模块的注释

在你的班级TargetNumberManager

  • 我会为 Dagger 模块使用普通的 Kotlin 类,并避免使用接口或数据类。因为您不会创建模块的实例:Dagger 将创建它。
  • 避免在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()
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:

  • 我删除了@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"
    }
}
Run Code Online (Sandbox Code Playgroud)

通过这些更改,TargetNumberManagerDagger 可以在您想要将其注入某处时创建它。它被标记为 Singleton,因此只要您注入它,Dagger 就会提供相同的实例。

当您尝试注入ITargetNumberManager. 因为你有一个接口和一个实现,但是同一个接口可能还有其他实现,所以我们需要告诉 Dagger 使用哪一个。我们通过使用@Binds注释来做到这一点。@Binds方法必须是抽象的或位于接口内部。这里我为 Dagger 模块使用了抽象类:

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

    @Binds
    abstract fun bindTargetNumberManager(impl: TargetNumberManager): ITargetNumberManager
}
Run Code Online (Sandbox Code Playgroud)

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

如何使用 EntryPoints 从 Dagger 获取实例

  • 在普通 Dagger 中,您可以创建组件接口。Hilt 包含普通 Dagger 中的所有内容,因此 Hilt 中也有组件。在希尔特有EntryPoint接口。Hilt发现这些接口并使组件接口扩展这些EntryPoint接口。使用 anEntryPoint基本上就像向组件接口添加功能一样。请参阅:https://dagger.dev/hilt/entry-points
  • 您的组件接口和您EntryPoint可以包含一个返回对象实例的函数。通过添加具有依赖项返回类型的函数,您只需要求 Dagger 为您创建一个实例即可。

典型的 EntryPoint 如下所示:

@EntryPoint
@InstallIn(SingletonComponent::class)
interface MyEntryPoint {
    fun getTargetNumberManager(): ITargetNumberManager
}
Run Code Online (Sandbox Code Playgroud)

当您需要时ITargetNumberManager:获取此实例MyEntryPoint然后获取以下实例ITargetNumberManager

val myEntryPoint: MyEntryPoint = EntryPoints.get(applicationContext, MyEntryPoint::class.java)
val targetNumberManager: ITargetNumberManager = myEntryPoint.getTargetNumberManager()
Run Code Online (Sandbox Code Playgroud)

就是这样!如果您想在课堂上这样做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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Hilt 需要知道应用程序上下文,因为它将 SingletonComponent 保留在应用程序类中。所以它会去那里获取组件。获取组件后,它将转换为您的 EntryPoint 接口。然后您可以调用添加到 EntryPoint 中的函数。

我的其他笔记:

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

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