目录
- What we are doing
- The mental model
- The Application class
- The Hilt components
- Hilt bindings
- Hilt modules
- @Binds
- @Provides
- Scoping
- Resources
代码
简介
- 我已经开始了我的下一个应用程序,这是一个Twitch客户端应用。这个系列将是我创建此应用时所面临的所有笔记和问题。
入门
- 我不会花任何时间来设置刀柄的依赖项。您可以在文档中找到HERE
我们在做什么
- 通过用刀柄注入依赖的力量,我们将从这种情况下采取代码:
class DataStoreViewModel(
application:Application,
):AndroidViewModel(application) {
private val tokenDataStore:TokenDataStore = TokenDataStore(application)
val twitchRepoImpl: TwitchRepo = TwitchRepoImpl()
}
- 对此:
@HiltViewModel
class DataStoreViewModel @Inject constructor(
private val twitchRepoImpl: TwitchRepo,
private val tokenDataStore:TokenDataStore
): ViewModel() {
}
- 我们的代码不仅看起来更干净,而且还可以更容易测试。
心理模型
- 如果您不熟悉依赖性注射,那么可能很难真正掌握Hilt在做什么。这就是为什么我喜欢我们这个心理模型:
- 本质上是剑柄会创建components,任何时候我们的代码需要依赖性,我们都可以告诉它是从刀柄组件中获得该依赖性的。
应用程序类
- 假设我们俩现在都有适当的依赖关系,下一步是用
@HiltAndroidApp
注释类。正如documentation中所述:
All apps that use Hilt must contain an Application class that is annotated with @HiltAndroidApp. @HiltAndroidApp triggers Hilt's code generation, including a base class for your application that serves as the application-level dependency container.
- 我们可以这样创建此类:
@HiltAndroidApp
class HiltApplication:Application() {
}
- 只需确保在
AndroidManifest.xml
文件中声明它:
<application
android:name=".di.HiltApplication"
Android入口点
-
要允许将刀具注入我们的代码中,我们必须用特定的注释向我们的类注释。可以找到注释的完整列表。但是,由于我们想将代码注入ViewModel,我们需要注释用
@HiltViewModel
表示ViewModel3。 -
重要:重要的是要注意,当您用刀柄注释注释班级时,您还必须注释所有依赖于它的类的类。这意味着,如果我们用
@HiltViewModel
注释ViewModel,则必须用@AndroidEntryPoint
和周围的活动来注释片段。
组件
- 用
@HiltViewModel
和@AndroidEntryPoint
注释我们的课程为每个注释的Android类生成一个单独的握力组件。这些组件将具有直接与他们注释的课程有关的生命周期。可以在HERE中找到解释生命周期的详细图 - 因此,要创建一个族组件,我们只需应用注释:
@HiltViewModel
class DataStoreViewModel(
application:Application,
):AndroidViewModel(application) {
private val tokenDataStore:TokenDataStore = TokenDataStore(application)
val twitchRepoImpl: TwitchRepo = TwitchRepoImpl()
}
Hilt Bindings
-
Binding是一个在documentation中使用的术语。因此,让我们定义它,我们可以将一个约束力视为一个对象,可以告知其应如何创建我们的依赖性。我们告诉Hilt通过将
@Inject constructor
添加到主构造函数中使用binding
。 - 现在,我们可以将
tokenDataStore
和twitchRepoImpl
移至主要构造函数:
@HiltViewModel
class DataStoreViewModel @Inject constructor(
private val twitchRepoImpl: TwitchRepo,
private val tokenDataStore:TokenDataStore
): ViewModel() {
}
-
这看起来可能不错,但尚不正常,这是因为我们没有定义任何绑定来告诉刀片如何创建这些依赖性。
-
根据您的需求,只需添加刀柄并添加
@Inject constructor
注释即可。尝试运行您的应用程序,看看它是否崩溃。如果您的应用程序崩溃,则需要创建更具体的绑定,我们可以通过Hilt modules 进行此功能。
模块
- 如前所述,Modules用于创建更具体的绑定,而Hilt将使用该绑定来创建我们的依赖性。要创建一个模块,我们需要创建一个新类并用2个特定注释(
@Module
和@InstallIn
:)注释它:
@Module //defines class as a module
@InstallIn(ViewModelComponent::class)
abstract class ViewModelModule {
}
-
@InstallIn
注释用于定义我们的模块安装的哪个刀片组件。然后,这些模块将提供有关如何创建绑定的信息。对于我们的ViewModelComponent::class
表示,该模块将存储在ViewModel Component中。
注入接口实例@binds
- 模块的全部要点是为剑柄提供适当的信息,以便可以创建绑定,然后将其用于创建适当的依赖关系实例。对于我的代码,我希望将刀具注入界面,
TwitchRepo
。我们可以将接口注入@Binds,就像这样:
@Module
@InstallIn(ViewModelComponent::class)
abstract class ViewModelModule {
@Binds
abstract fun bindsTwitchRepo(
twitchRepoImpl: TwitchRepoImpl
):TwitchRepo
}
- 使用
@Binds
注释,我们创建一个抽象功能,并提供两个信息:
1)函数返回类型:这告诉刀片我们的函数提供了什么界面。
2)函数参数:这告诉HILT提供哪种实现。
- 使用我们在模块中创建的这种新依赖性,我们告诉Hilt,任何时候viewModel需要一个
TwitchRepo
实例,它应该实例化TwitchRepoImpl
并将其传递给我们的代码。请注意我如何说的That anytime a ViewModel
,此依赖性仅在视图模式中可用 - 现在我们拥有基础知识,我们可以变得更复杂
注入@provides 的实例
- 与
@Binds
一起,我们还可以在模块内使用另一种称为@Provides
的注释。通常,我们将使用提供的绑定注释,原因有两个。1) we do not own the class we want Hilt to instantiate
或2) we want to provide more details to Hilt
。最终,代码看起来像这样:
@Module
@InstallIn(SingletonComponent::class)
object SingletonModule {
@Singleton
@Provides
fun providesTwitchClient(): TwitchClient {
return Retrofit.Builder()
.baseUrl("https://api.twitch.tv/helix/")
.addConverterFactory(GsonConverterFactory.create())
.build().create(TwitchClient::class.java)
}
@Singleton
@Provides
fun providesTokenDataStore(
@ApplicationContext appContext: Context
): TokenDataStore {
return TokenDataStore(appContext)
}
}
-
正如我们之前提到的,
-
@InstallIn(SingletonComponent::class)
意味着所有这些依赖关系都将存储在SingletonComponent
中。根据component hierarchy,这意味着我们的这些依赖项将适用于我们所有的代码。现在我们需要谈论如何范围
范围
-
在文档和博客文章中,您会不断看到报价:
By default, all bindings in Hilt are unscoped. This means that each time your app requests the binding, Hilt creates a new instance of the needed type.
,因此即使在我们的SingletonModule
中,每当抓地力创建一个实例TwitchClient
或TokenDataStore
时,这都是一个新实例。这不是我们想要的。为了解决此问题,我们需要将依赖项范围范围范围范围范围,以@Singleton
的注释完成。这告诉HILT仅创建TwitchClient
和TokenDataStore
,然后重复使用相同的实例。 -
值得指出的是,只有在您的代码功能(对于我的代码)功能时,才应使用
@Singleton
注释。我只想要一个Retrofit
实例,而TokenDataStore
是一个仅允许一个实例的Datastore。
资源
结论
- 感谢您抽出宝贵的时间阅读我的博客文章。如果您有任何疑问或疑虑,请在下面发表评论或在Twitter上与我联系。