有没有办法在非(Activity、服务、片段、应用程序)class 中使用注入器

Is there a way to use injectors in a non-(Activity,Service,Fragment, Application) class

我们在我们的应用程序中使用 Dagger2。我正在尝试创建一个房间数据库,并且正在编写存储库代码,但我想为 class 注入应用程序上下文和 DAO。

我感觉你只能在 Fragments、Activity、Services、Applications 等中进行 Dagger 注入

这是我的资料:

class DownloadsDataRepositoryImpl : IDownloadsDataRepository, HasAndroidInjector {

      @Inject
      lateinit var androidInjector : DispatchingAndroidInjector<Any>

      @Inject
      lateinit var downloadsDao: DownloadsDao

      override fun androidInjector(): AndroidInjector<Any> = androidInjector
      init { 
         androidInjector()
      }

}

但我确定它不会起作用。有办法吗?

不确定您是如何实施匕首的,但这里有一个示例,您可以如何向非 activity class.

提供上下文

假设您有 AppModule class,那么您可以在其中添加 provideContext() 方法:

@Module
class AppModule(app: App) {

    private var application: Application = app

    @Provides
    fun provideContext(): Context {
        return application
    }
}

这里是非 activity class 用 Kotlin 编写的:

class Utils @inject constructor(private val context: Context) { .. }

就是这样,只需重建 j

如前所述,dagger-android 只是一个帮助注入特定框架的工具 类,您无法控制它的创建。

正确的方法是使用简单的

为了更直接地说明如何在 @Component 上公开它,我需要更多代码,特别是关于 activity/fragment 上的代码,但这是一个粗略的示例(即我没有测试,如果有小错误,你可以按照编译错误信息修复它们):

首先,您将拥有一些公开您的 DAO 的对象。大概是房间吧?

@Entity(tableName = "download_table")
data class DownloadEntity(
    @PrimaryKey
    val key: String
)

@Dao
interface DownloadsDao {
    @Query("SELECT * FROM download_table")
    fun load(): List<DownloadEntity>
}

@Database(
    entities = [DownloadEntity::class], version = 1
)
abstract class DownloadRoomDatabase : RoomDatabase() {
    abstract val downloadsDao: DownloadsDao
}

现在我们将创建一个带有 @Inject 注释的原始存储库。 Dagger 会负责为我们构建这个对象。请注意,我没有为此使用 dagger-android:

interface IDownloadsDataRepository

class DownloadsDataRepositoryImpl @Inject constructor(
    val downloadsDao: DownloadsDao
) : IDownloadsDataRepository

如何将其公开给您的 activity/fragment/服务需要有关您的实施的更多详细信息。例如,如果它位于用 @Inject 注释的 ViewModelPresenter 内,或者您直接访问 activity 将导致不同的实现。没有更多细节,我假设您直接在 activity:

上访问存储库
class DownloadActivity : FragmentActivity() {

    @Inject
    lateinit val repo: IDownloadsDataRepository

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        DaggerDownloadComponent.factory().create(this).inject(this)
    }
}

现在我们需要指导 Dagger 如何:

  1. 将具体的 DownloadsDataRepositoryImpl 绑定到 activity 需要的 IDownloadsDataRepository 接口
  2. 如何提供构建 DownloadsDataRepositoryImpl 的依赖项

为此我们需要 module:

@Module
abstract class RepositoryModule {

    //We will bind our actual implementation to the IDownloadsDataRepository
    @Binds
    abstract fun bindRepo(repo: DownloadsDataRepositoryImpl): IDownloadsDataRepository


    @Module
    companion object {

        //We need the database to get access to the DAO
        @Provides
        @JvmStatic
        fun provideDataBase(context: Context): DownloadRoomDatabase =
            Room.databaseBuilder(
                context,
                DownloadRoomDatabase::class.java,
                "download_database.db"
            ).build()

        //With the database, we can provide the DAO:
        @Provides
        @JvmStatic
        fun provideDao(db: DownloadRoomDatabase): DownloadsDao = db.downloadsDao
    }
}

这样,我们就可以完成拼图的最后一部分,创建 @Component:

@Component(
    modules = [
        RepositoryModule::class
    ]
)
interface DownloadComponent {

    fun inject(activity: DownloadActivity)

    @Component.Factory
    interface Factory {
        fun create(context: Context): DownloadComponent
    }
}

请注意,我没有使用任何 dagger-android 代码,我认为它没有用,而且会造成不必要的混乱。坚持使用基本的 dagger2 构造,你就没事了。您可以实现 99.9% 的应用程序,只需了解这些构造的工作原理:

@Module@Component@Subcomponent

编辑:如评论中所述,您可能需要正确管理存储库的 scope,特别是如果您实际使用 Room 时的数据库创建.

I have a feeling that you can only do Dagger injection in Fragments, Activities, Services, Applications, etc.

你假设 Dagger-Android 2.20 之前是正确的,但在 2.20+ 之后就不是了。

现在您可以为任何 class 创建一个 @ContributesAndroidInjector,这将为您添加 @ContributesAndroidInjectorT 生成一个 AndroidInjector<T>

这意味着有一个多重绑定,允许您为 T 获得一个 AndroidInjector<T>,这就是 HasAndroidInjector 为您所做的。

因此以下内容在不同的场景中对我有用(对于工作管理器中的成员注入工人,而不是创建多重绑定和工厂):

@Keep
class SyncWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
    init {
        val injector = context.applicationContext as HasAndroidInjector
        injector.androidInjector().inject(this)
    }

    @Inject
    lateinit var apiService: ApiService

@ContributesAndroidInjector
abstract fun syncWorker(): SyncWorker

但是在您的特定情况下,none 是必需的。

Dagger-Android 用于使用自动生成的子组件注入成员 classes,通常只有当注入的类型位于不同的模块中时才需要,因此您可以'不要直接将 fun inject(T t) 添加到您的 AppComponent 中,否则您看不到您的 AppComponent.

在您的情况下,简单的构造函数注入就足够了,因为您拥有自己的 class。

@Singleton
class DownloadsDataRepositoryImpl @Inject constructor(
    private val downloadsDao: DownloadsDao
): IDownloadsDataRepository {}

您可以通过模块绑定

@Module
abstract class DownloadsModule {
    @Binds
    abstract fun dataRepository(impl: DownloadsDataRepositoryImpl): IDownloadsDataRepository
}

否则你只需在 Application.onCreate()

中创建你的组件实例
@Component(modules = [DownloadsModule::class])
@Singleton
interface AppComponent {
    fun dataRepository(): DownloadsDataRepository

    @Component.Factory
    interface Factory {
        fun create(@BindsInstance appContext: Context): AppComponent
    }
}

class CustomApplication: Application() {
    lateinit var component: AppComponent
        private set

    override fun onCreate() {
        super.onCreate()
        component = DaggerAppComponent.factory().create(this)
    }
}

然后就可以得到了

val component = (context.applicationContext as CustomApplication).component

尽管从技术上讲您也可以创建一个扩展函数

val Context.appComponent: AppComponent
    get() = (applicationContext as CustomApplication).component

val component = context.appComponent