有没有办法在非(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
注释的 ViewModel
或 Presenter
内,或者您直接访问 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 如何:
- 将具体的 DownloadsDataRepositoryImpl 绑定到 activity 需要的 IDownloadsDataRepository 接口
- 如何提供构建 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
,这将为您添加 @ContributesAndroidInjector
的 T
生成一个 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
我们在我们的应用程序中使用 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
注释的 ViewModel
或 Presenter
内,或者您直接访问 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 如何:
- 将具体的 DownloadsDataRepositoryImpl 绑定到 activity 需要的 IDownloadsDataRepository 接口
- 如何提供构建 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
,这将为您添加 @ContributesAndroidInjector
的 T
生成一个 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