Android 是否有一种在非(活动、服务、片段、应用程序)类中使用注入器的方法

Android 是否有一种在非(活动、服务、片段、应用程序)类中使用注入器的方法,android,dagger-2,Android,Dagger 2,我们在应用程序中使用Dagger2。我正在尝试创建一个room数据库,并且正在编写存储库代码,但是我想为类注入应用程序上下文和DAO 我有一种感觉,您只能在片段、活动、服务、应用程序等中进行匕首注入 以下是我所拥有的: class DownloadsDataRepositoryImpl : IDownloadsDataRepository, HasAndroidInjector { @Inject lateinit var androidInjector : Dispa

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

我有一种感觉,您只能在片段、活动、服务、应用程序等中进行匕首注入

以下是我所拥有的:

class DownloadsDataRepositoryImpl : IDownloadsDataRepository, HasAndroidInjector {

      @Inject
      lateinit var androidInjector : DispatchingAndroidInjector<Any>

      @Inject
      lateinit var downloadsDao: DownloadsDao

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

}

但我肯定这是行不通的。有办法吗?

不确定您是如何实现dagger的,但下面是一个示例,说明如何为非活动类提供上下文

假设您有AppModule类,因此可以在其中添加ProviderContext方法:

@Module
class AppModule(app: App) {

    private var application: Application = app

    @Provides
    fun provideContext(): Context {
        return application
    }
}
下面是用Kotlin编写的非活动类:

类Utils@injectconstructorprivate val context:context{ .. }


就这样,只需重新构建j

就可以了,我不确定您是如何实现dagger的,但这里有一个示例,说明如何为非活动类提供上下文

假设您有AppModule类,因此可以在其中添加ProviderContext方法:

@Module
class AppModule(app: App) {

    private var application: Application = app

    @Provides
    fun provideContext(): Context {
        return application
    }
}
下面是用Kotlin编写的非活动类:

类Utils@injectconstructorprivate val context:context{ .. }


就是这样,就像前面所说的,dagger android只是一个工具,可以帮助注入特定的框架类,而你无法控制它的创建

正确的方法是使用simple

为了更直接地说明如何在@Component上公开它,我需要更多的代码,特别是关于活动/片段的代码,但这里有一个我没有测试过的粗略示例,如果有小错误,您可以按照编译器错误消息修复它们:

首先,您将有一些对象公开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注释的ViewModel或Presenter中,或者您直接在活动上访问它,则会导致不同的实现。如果没有更多详细信息,我将假设您直接在活动中访问存储库:

class DownloadActivity : FragmentActivity() {

    @Inject
    lateinit val repo: IDownloadsDataRepository

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        DaggerDownloadComponent.factory().create(this).inject(this)
    }
}
现在我们需要指导Dagger如何:

将您的具体下载SDatarepositoryImpl绑定到活动所需的IDownloadsDataRepository接口 如何提供依赖项以构建DownloadsDataRepositoryImpl 为此,我们需要:

这样,我们就可以完成谜题的最后一部分,创建@组件:

请注意,我并没有使用任何dagger android代码,我认为它并没有什么用处,而且会造成更多的混乱。坚持基本的匕首2结构,你就没事了。只有了解这些结构的工作原理,您才能实现99.9%的应用程序:

@模块、@组件和@子组件


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

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

正确的方法是使用simple

为了更直接地说明如何在@Component上公开它,我需要更多的代码,特别是关于活动/片段的代码,但这里有一个我没有测试过的粗略示例,如果有小错误,您可以按照编译器错误消息修复它们:

首先,您将有一些对象公开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注释的ViewModel或Presenter中,或者您直接在活动上访问它,则会导致不同的实现。如果没有更多详细信息,我将假设您直接在活动中访问存储库:

class DownloadActivity : FragmentActivity() {

    @Inject
    lateinit val repo: IDownloadsDataRepository

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        DaggerDownloadComponent.factory().create(this).inject(this)
    }
}
现在我们需要指导Dagger如何:

将您的具体下载SDatarepositoryImpl绑定到活动所需的IDownloadsDataRepository接口 如何提供依赖项以构建DownloadsDataRepositoryImpl 为此,我们需要:

这样,我们就可以完成谜题的最后一部分,创建@组件:

请注意,我并没有使用任何dagger android代码,我认为它并没有什么用处,而且会造成更多的混乱。坚持基本的匕首2结构,你就没事了。只有了解这些结构的工作原理,您才能实现99.9%的应用程序:

@模块、@组件和@子组件

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

我有一种感觉,您只能在片段、活动、服务、应用程序等中进行匕首注入

你认为Dagger Android 2.20之前是正确的,但2.20+之后不是

现在你可以创建一个@C了 任何类的OnDributeSandroidInjector,它将为您添加@ContributesAndroidInjector的T生成一个AndroidInjector

这意味着有一个多重绑定,可以让你得到一个T的AndroidJector,这就是HasAndroidJector为你做的

因此,对于work manager中的成员注入工作者,以下内容在不同的场景中适用于我,而不是创建多重绑定和工厂:

@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

然而,在您的特殊情况下,这些都不是必需的

Dagger Android适用于使用自动生成的子组件的成员注入类,通常只有在注入类型位于不同模块内时才需要该子组件,因此无法直接将有趣的注入添加到AppComponent中,或者看不到AppComponent

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

@Singleton
class DownloadsDataRepositoryImpl @Inject constructor(
    private val downloadsDao: DownloadsDao
): IDownloadsDataRepository {}
您可以通过一个模块绑定它

@Module
abstract class DownloadsModule {
    @Binds
    abstract fun dataRepository(impl: DownloadsDataRepositoryImpl): IDownloadsDataRepository
}
否则,您只需在Application.onCreate内创建组件实例

然后你就可以得到它了

val component = (context.applicationContext as CustomApplication).component
虽然从技术上讲,您也可以创建一个扩展函数

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

val component = context.appComponent
我有一种感觉,您只能在片段、活动、服务、应用程序等中进行匕首注入

你认为Dagger Android 2.20之前是正确的,但2.20+之后不是

现在,您可以为任何类创建@ContributesAndroidInjector,它将为您添加了@ContributesAndroidInjector的T生成一个AndroidInjector

这意味着有一个多重绑定,可以让你得到一个T的AndroidJector,这就是HasAndroidJector为你做的

因此,对于work manager中的成员注入工作者,以下内容在不同的场景中适用于我,而不是创建多重绑定和工厂:

@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

然而,在您的特殊情况下,这些都不是必需的

Dagger Android适用于使用自动生成的子组件的成员注入类,通常只有在注入类型位于不同模块内时才需要该子组件,因此无法直接将有趣的注入添加到AppComponent中,或者看不到AppComponent

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

@Singleton
class DownloadsDataRepositoryImpl @Inject constructor(
    private val downloadsDao: DownloadsDao
): IDownloadsDataRepository {}
您可以通过一个模块绑定它

@Module
abstract class DownloadsModule {
    @Binds
    abstract fun dataRepository(impl: DownloadsDataRepositoryImpl): IDownloadsDataRepository
}
否则,您只需在Application.onCreate内创建组件实例

然后你就可以得到它了

val component = (context.applicationContext as CustomApplication).component
虽然从技术上讲,您也可以创建一个扩展函数

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

val component = context.appComponent

在问你是否可以之前,先问问你是否应该。对活动等使用字段注入的原因是框架要求它们具有无参数构造函数。房间存储库是否需要无参数构造函数?如果没有,就使用构造函数注入。在询问是否可以之前,先询问是否应该。对活动等使用字段注入的原因是框架要求它们具有无参数构造函数。房间存储库是否需要无参数构造函数?如果没有,就使用构造函数注入。要实现这一点,您需要创建一个新的组件实例,并且根本不使用任何作用域。如果我确实需要作用域提供程序呢?当然,我不能仅仅在Activity.onCreate中创建一个单例组件。这个答案假设存储库是无状态的,DB实例没有创建开销,因为每次轮换时都会重新创建它,等等。虽然确实是这样,QA希望使用构造函数注入。所有相关的要点,但我认为解释作用域是他问题的一个附属概念。将此添加到答案中需要进一步解释,这在我在回答中用作参考的第二个链接中得到了更好的解释。正如我在回答中所述,这不应该是一个功能实现,而只是一个关于如何注入其存储库而不是依赖dagger-android的草图。要实现这一点,您创建了一个新的组件实例,并且根本不使用任何作用域。如果我确实需要作用域提供程序呢?当然,我不能仅仅在Activity.onCreate中创建一个单例组件。这个答案假设存储库是无状态的,DB实例没有创建开销,因为每次轮换时都会重新创建它,等等。虽然确实是这样,QA希望使用构造函数注入。所有相关的要点,但我认为解释作用域是他问题的一个附属概念。将此添加到答案中需要进一步解释,在我的答案中用作参考的第二个链接中有更好的解释。正如我在回答中所述,这不应该是功能实现,但这只是一个关于如何注入他的存储库而不是依赖dagger android的草图。我想你忘了向他解释如何公开你的组件了。如果它是一个私有变量,你打算如何在你的活动/片段/等中使用它?我想你忘了向他解释如何公开你的组件。如果是pri vate变量,您打算如何在活动/片段等中使用它?