Dependency Injection is the first step you have to take towards building an app that can be tested. So today we will use Hilt to introduce view model injection into a Fragment. If you wish to carry along with this article you can checkout the starting code here
依赖注入是构建可测试应用程序的第一步。 因此,今天我们将使用Hilt将视图模型注入片段中。 如果您希望继续阅读本文,可以在此处签出起始代码
First of all we need to add dependencies.
首先,我们需要添加依赖项。
Add the hilt-android-gradle-plugin
plugin to your project's root build.gradle
file:
将hilt-android-gradle-plugin
插件添加到项目的根build.gradle
文件中:
buildscript {
...
dependencies {
...
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha'
}
}
Then, apply the Gradle plugin and add these dependencies in your app/build.gradle
file:
然后,应用Gradle插件并将以下依赖项添加到您的app/build.gradle
文件中:
...
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'
dependencies {
implementation "com.google.dagger:hilt-android:2.28-alpha"
kapt "androidx.hilt:hilt-compiler:1.0.0-alpha02"
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02"
}
Hilt uses Java 8 features. To enable Java 8 in your project, add the following to the app/build.gradle
file:
Hilt使用Java 8功能 。 要在项目中启用Java 8,请将以下内容添加到app/build.gradle
文件中:
android {
...{sourceCompatibility = 1.8
targetCompatibility = 1.8}kotlinOptions {jvmTarget = "1.8"}}
If you’re following along, now’s the time to sync your gradle files.
如果您正在遵循,现在是时候同步gradle文件了。
All apps that use Hilt must contain an Application
class that is annotated with @HiltAndroidApp
.
所有使用Hilt的Application
必须包含一个@HiltAndroidApp
注释的Application
类。
@HiltAndroidApp
class ExampleApplication : Application() { ... }
This triggers Hilt’s code generation, including a base class for your application that serves as the application-level dependency container.
这将触发Hilt的代码生成,包括应用程序的基类,该基类充当应用程序级依赖项容器。
This part is taken as is from official documentation. You can check it out here.
本部分摘自官方文档。 您可以在这里查看。
https://developer.android.com/training/dependency-injection/hilt-android
https://developer.android.com/training/dependency-injection/hilt-android
This concludes the setting up part. If you do not wish to go through the setup steps, you can check out code for a sample application with basic setup complete here
至此结束设置部分 。 如果您不想执行设置步骤,则可以在此处完成基本设置的情况下签出示例应用程序的代码
In this app I am trying to build a user interface browse any RSS feed.
在这个程序中,我试图建立一个用户界面,浏览任何RSS feed。
When we open the app, we see MyChannelsFragment.kt. To show data in this fragment we have a ViewModel MyChannelsViewModel. This view model will get it’s data from a repository MyChannelRepository.
打开应用程序时,我们会看到MyChannelsFragment.kt。 为了显示此片段中的数据,我们有一个ViewModel MyChannelsViewModel。 该视图模型将从存储库MyChannelRepository中获取数据。
It is the responsibility of repository to get data from the local storage or from a remote server.
存储库负责从本地存储或远程服务器获取数据。
In this tutorial, we will inject MyChannelRepository into MyChannelsViewModel. And then MyChannelsViewModel will be injected into MyChannelsFragment.
在本教程中,我们将把MyChannelRepository注入MyChannelsViewModel中。 然后MyChannelsViewModel将注入MyChannelsFragment。
Let’s get started.
让我们开始吧。
Hilt can provide dependencies to classes that have @AndroidEntryPoint
annotation. so we add this to our fragment.
Hilt可以为具有@AndroidEntryPoint
批注的类提供依赖@AndroidEntryPoint
。 因此我们将其添加到片段中。
@AndroidEntryPoint
class MyChannelsFragment : Fragment() {
.....
}
Since fragment is used in HomeActivity, HomeActivity would also have to be annotated with @AndroidEntryPoint.
由于片段在HomeActivity中使用, 因此 HomeActivity也必须使用@AndroidEntryPoint进行注释。
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
...
}
Now we need to tell Hilt that we wish to inject our view model. Since our viewmodel will be dependent upon implementation of MyChannelRepository i.e. MyChannelRepositoryImpl we will have to tell Hilt how to inject this transitive dependency.
现在我们需要告诉Hilt我们希望注入我们的视图模型。 由于我们的视图模型将依赖于MyChannelRepository的实现,即MyChannelRepositoryImpl,我们将不得不告诉Hilt如何注入这种传递依赖。
Currently MyChannelsViewModel class looks like this
当前MyChannelsViewModel类看起来像这样
class MyChannelsViewModel (private val myChannelRepository: MyChannelRepository) : ViewModel() {
val suggestedChannels = myChannelRepository.getSuggestedChannels()
}
Now add @ViewModelInject annotation to view model to mark it’s constructor for injection. MyChannelsViewModel class will now look like this
现在添加@ViewModelInject批注以查看模型,以标记其构造函数以进行注入。 MyChannelsViewModel类现在将如下所示
class MyChannelsViewModel @ViewModelInject constructor(private val myChannelRepository: MyChannelRepository) : ViewModel() {
val suggestedChannels = myChannelRepository.getSuggestedChannels()
}
Now all that’s left is to tell Hilt how MyChannelRepository instance will be provided to MyChannelsViewModel.
现在剩下的就是告诉Hilt如何将MyChannelRepository实例提供给MyChannelsViewModel。
To accomplish this, we need to create a module class that will tell hilt how to provide an implementation of MyChannelRepository.
为此,我们需要创建一个模块类,该类将告诉hilt如何提供MyChannelRepository的实现。
Since MyChannelRepository is an interface it’s constructor cannot be called directly. Having interfaces exposed to view model allows us to expose only behavior/data to the view model which does not need to know how the repository will fetch data.
从MyChannelRepository开始 是一个接口,不能直接调用其构造函数。 将接口公开给视图模型可以使我们仅向视图模型公开行为/数据,而无需知道存储库将如何获取数据。
Create a new package di and in this package create a new class called RepositoryModule. This class will look like this
创建一个新的包di 并在此包中创建一个名为RepositoryModule的新类。 这个课看起来像这样
package com.rssreader.di
import com.rssreader.ui.channels.ChannelRepositoryImpl
import com.rssreader.ui.channels.MyChannelRepository
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityComponent
//Repositories will live same as the activity that requires them
@InstallIn(ActivityComponent::class)
abstract class RepositoryModule {
@Binds
abstract fun providesChannelRepository(impl: ChannelRepositoryImpl): MyChannelRepository
}
Lets examine each part of this class in detail :
让我们详细检查该类的每个部分:
@Module
This annotation tells hilt that this is a class that contributes to providing objects for injection.
这个注释告诉hilt这是一个有助于提供注入对象的类。
@InstallIn(ActivityComponent::class)
In simple terms this tells Hilt to keep the objects in this class alive as long as the activity that requested them is alive.
简单来说,这告诉Hilt只要请求它们的活动还处于活动状态,就可以使此类中的对象保持活动状态。
@Binds
abstract fun providesChannelRepository(impl: ChannelRepositoryImpl): MyChannelRepository
To tell Hilt what implementation to use for an interface, you can use the @Binds annotation on a function inside a Hilt module.
要告诉Hilt接口使用哪种实现,可以在Hilt模块内部的函数上使用@Binds批注。
The function providesChannelRepository (in simple terms) tells Hilt to use an instance of ChannelRepositoryImpl whenever an injection operation requires an object of type MyChannelRepository.
功能providesChannelRepository(在简单的术语)告诉剑柄每当注入操作需要类型MyChannelRepository的一个目的是使用ChannelRepositoryImpl的一个实例。
Before this arrangement works, we will have to tell Hilt how to produce an instance of ChannelRepositoryImpl. To acheive this add @Inject constructor to ChannelRepositoryImpl.
在此安排生效之前,我们将不得不告诉Hilt如何生成ChannelRepositoryImpl的实例。 要实现此目的,请将@Inject构造函数添加到ChannelRepositoryImpl。
The class will now look like this
该类现在看起来像这样
class ChannelRepositoryImpl @Inject constructor() : MyChannelRepository {
...
}
Now hilt can instantiate our view model when it has to be injected. Now we just need to add our view model to the MyChannelsFragment. We can do that by
现在,hilt可以在必须注入时实例化我们的视图模型。 现在,我们只需要将视图模型添加到MyChannelsFragment。 我们可以做到
val viewModel by viewModels<MyChannelsViewModel>()
And it’s done. Now Hilt can inject MyChannelsViewModel into MyChannelsFragment.
完成了。 现在,Hilt可以将MyChannelsViewModel注入MyChannelsFragment。
To get a better handle of Hilt and its anootations checkout this awesome codelab
为了更好地了解Hilt及其修饰,请查看此出色的代码实验室
If you wish to check out the final code for this article, you can find the finished code here.
如果您想查看本文的最终代码,可以在这里找到完成的代码。
翻译自: https://medium.com/swlh/inject-viewmodel-using-hilt-2c968f1e85fe