一、Hilt 干依赖注入的
Hilt是干嘛的
Hilt,一个依赖注入框架,谷歌搞出来的,基于Dagger,但是更加简洁易用。
什么是依赖注入 (Dependency Injection)
依赖注入是一种设计模式。主要就是解耦!解耦!解耦!。
依赖注入是一种设计模式,它通过在对象之间传递依赖关系,使得对象能够更灵活、可测试和可维护。在Android开发中,依赖注入可以帮助我们管理和解耦应用程序中的各种组件,如Activity、Fragment、ViewModel、Repository等。
既生Dagger 何生 Hilt ?
安卓中的Hilt是一种用于依赖注入(Dependency Injection)的库。Hilt是由Google开发的,它是基于Dagger的一个扩展库,旨在简化Android应用程序中的依赖注入过程。
Hilt提供了一套注解和生成代码,用于在Android应用程序中自动完成依赖注入的配置。它简化了Dagger的使用,减少了样板代码的编写,提高了开发效率。
Hilt还提供了一些特定于Android的功能,如对Android组件的注入支持
,以及在不同生命周期中管理依赖关系的能力
。
Hilt 大概怎么用
- Application 注解下
- 在需要的标记为依赖注入的Activity、Fragment注解下
@AndroidEntryPoint
使用Hilt进行依赖注入时,首先需要在应用程序的Application类上添加@HiltAndroidApp
注解,以告知Hilt该应用程序将使用Hilt进行依赖注入。然后,可以在其他组件(如Activity、Fragment)中使用@AndroidEntryPoint
注解来标记需要进行依赖注入的地方。Hilt会根据注解生成必要的代码,在运行时完成依赖注入。
Hilt还提供了一些其他的注解,用于标记和配置依赖关系。例如,
- 可以使用
@Inject
注解来标记需要注入的依赖项, - 使用
@Module
注解来标记用于提供依赖项的模块类, - 使用
@Provides
注解来标记提供依赖项的方法等。
二、什么是TM的依赖注入,给个例子
比如我们创建一辆汽车,汽车需要引擎。
- 如果
不用依赖注入
,创建汽车,汽车内部还要创建引擎
,这样就耦合 耦合 偶尔
- 如果
使用依赖注入
,创建汽车不需要创建引擎
,而是从外部将Engine
对象传递
给Car
类,这样就解耦 解耦 解耦
不使用依赖注入,汽车内部创建引擎,耦合耦合耦合,
class Engine {
void start() {
System.out.println("Engine started");
}
}
class Car {
private Engine engine;
Car() {
engine = new Engine(); // Car类自己创建Engine对象
}
void drive() {
engine.start();
System.out.println("Car is driving");
}
}
使用依赖注入,传递引擎对象给汽车,汽车内部不创建引擎对象,解耦解耦解耦
class Car {
private Engine engine;
Car(Engine engine) { // Engine对象通过构造函数注入
this.engine = engine;
}
void drive() {
engine.start();
System.out.println("Car is driving");
}
}
现在,我们可以在创建Car
对象时,把一个Engine
对象传递给它:
Engine engine = new Engine();
Car car = new Car(engine);
car.drive();
上面的对比。虽然简单,但是是个挺好的例子
"Engine对象通过构造函数注入"是指我们通过Car
类的构造函数将Engine
对象传递给Car
类。这是一种依赖注入的方式,被称为构造函数注入。
在这个例子中,Car
类需要一个Engine
对象来工作。在没有使用依赖注入的情况下,Car
类会自己创建一个Engine
对象。但这样做的问题是,Car
类与Engine
类紧密耦合在一起,如果我们想要更换一个不同类型的Engine
,或者我们想要在测试时使用一个模拟的Engine
,我们就需要修改Car
类的代码。
当我们使用构造函数注入时,我们不再在Car
类内部创建Engine
对象,而是在创建Car
对象时,从外部将Engine
对象传递给Car
类。这样,Car
类就不再依赖于Engine
类的具体实现,我们可以很容易地更换不同类型的Engine
,或者在测试时使用一个模拟的Engine
,而不需要修改Car
类的代码。
也许你会说,不就是传个参吗,说得这么麻烦???? 某种程度上,你可以这么说,当时,这个玩意,能放大了玩。
依赖注入,可不仅仅是高级的传参 (李姐会理解)
人家,可传参,牛逼多了。
-
Activity和Fragment:在Android开发中,Activity和Fragment经常需要访问一些共享的资源或服务,例如
网络请求Retrofit
、数据库访问、ViewModel等。如果没有依赖注入,我们可能需要在每个Activity或Fragment中手动创建这些对象,这会导致代码重复,而且使得Activity和Fragment与这些服务紧密耦合,难以进行单元测试。通过使用依赖注入,我们可以在一个统一的地方配置这些服务,然后在需要的地方自动注入,这使得代码更加清晰,更容易测试。
-
Activity和Fragment:在Android开发中,Activity和Fragment经常需要访问一些共享的资源或服务,例如
- 主App和Module:在一个模块化的应用中,不同的模块可能需要访问一些共享的服务。如果没有依赖注入,我们可能需要通过一些复杂的方式来让这些模块获取到这些服务,这会使得代码变得复杂,而且难以管理。通过使用依赖注入,我们可以在主App中配置这些服务,然后在各个模块中自动注入,这使得代码更加清晰,更容易管理。
- 3、管理单例依赖项:在 Android 应用程序中,有一些依赖项是单例的,如数据库、网络客户端等。使用 Hilt 可以更轻松地管理这些单例依赖项,同时避免了手动管理单例依赖项的复杂性。
这个说是不是不会觉得是简答传参了!
依赖注入使得我们的代码更加模块化,每个类都只关注自己的职责,不需要关心其依赖对象的创建和管理。这使得我们的代码更容易重用。
三、Hilt的一些常见注解
在开始app例子之前,还需要需要将一些注解前面说明一下。
Hilt使用了一系列的注解来简化依赖注入的过程。以下是一些最常用的Hilt注解:
@HiltAndroidApp
-
@HiltAndroidApp
: 这个注解用于Application类,它会触发Hilt的代码生成,包括一个应用级别的组件,这个组件可以为其他Hilt组件(如Activity组件)提供依赖。
@HiltAndroidApp
class MyApplication : Application()
@AndroidEntryPoint
-
@AndroidEntryPoint
: 这个注解用于Android组件,如Activity、Fragment、Service等,它告诉Hilt这些组件可以接收依赖注入。
@AndroidEntryPoint class MainActivity : AppCompatActivity() { @Inject lateinit var someClass: SomeClass }
@Inject
-
@Inject
: 这个注解用于字段、构造函数或方法,告诉Hilt需要注入依赖。对于字段,Hilt会自动注入相应的依赖;对于构造函数,Hilt会使用它来创建类的实例;对于方法,Hilt会在注入后调用它。
class SomeClass @Inject constructor(private val someDependency: SomeDependency)
@Module
-
@Module
: 这个注解用于对象,这些对象提供了一系列的依赖提供方法。这些方法用@Provides
注解标记,Hilt会在需要时调用它们。
@Provides
-
@Provides
: 这个注解用于在@Module
注解的类中的方法,这些方法提供了依赖的实例。Hilt会在需要时调用这些方法。
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Singleton
@Provides
fun provideSomeDependency(): SomeDependency {
return SomeDependency()
}
}
@InstallIn
-
@InstallIn
: 这个注解用于@Module
注解的类,指定这个模块安装在哪个Hilt组件中。
@Singleton
-
@Singleton
: 这个注解用于@Provides
注解的方法或@Inject
注解的构造函数,告诉Hilt提供的依赖是单例的。
@Singleton
class SomeSingletonClass @Inject constructor()
@ViewModelInject
-
@ViewModelInject
: 这个注解用于ViewModel的构造函数,告诉Hilt如何创建ViewModel的实例。
class MyViewModel @ViewModelInject constructor(private val repository: Repository) : ViewModel()
@Assisted
- @Assisted:用于标记 ViewModel 的构造函数参数,以便在使用 assisted injection 时注入这些参数。
@AssistedInject
- @AssistedInject:用于标记使用 assisted injection 创建的 ViewModel 的构造函数。
这些注解使得Hilt能够自动处理依赖的创建和注入,大大简化了依赖注入的过程。
注解 | 描述 |
---|---|
@HiltAndroidApp |
用于Application类,触发Hilt的代码生成,包括一个应用级别的组件,这个组件可以为其他Hilt组件(如Activity组件)提供依赖。 |
@AndroidEntryPoint |
用于Android组件,如Activity、Fragment、Service等,告诉Hilt这些组件可以接收依赖注入。 |
@Inject |
用于字段 、构造函数 或方法 ,告诉Hilt需要注入依赖。对于字段,Hilt会自动注入相应的依赖 ;对于构造函数,Hilt会使用它来创建类的实例 ;对于方法,Hilt会在注入后调用它 。 |
@Module |
用于对象,这些对象提供了一系列的依赖提供方法。这些方法用@Provides 注解标记,Hilt会在需要时调用它们。 |
@Provides |
用于在@Module 注解的类中的方法,这些方法提供了依赖的实例。Hilt会在需要时调用这些方法。 |
@InstallIn |
用于@Module 注解的类,指定这个模块安装在哪个Hilt组件中。 |
@Singleton |
用于@Provides 注解的方法或@Inject 注解的构造函数,告诉Hilt提供的依赖是单例的。 |
@ViewModelInject |
用于ViewModel的构造函数,告诉Hilt如何创建ViewModel的实例。 |
-
@Module
和@Provides
:这两个注解通常一起使用,定义在一个类中,这个类提供了一系列的依赖提供方法。这些方法用@Provides
注解标记,Hilt会在需要时调用它们。
四、上个App
四、1、hilt准备
app目录下 build.gradle
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
// 添加kapt注解处理器
id 'kotlin-kapt'
// hilt所需
id 'com.google.dagger.hilt.android'
}
dependencies {
implementation 'com.google.dagger:hilt-android:2.44'
kapt 'com.google.dagger:hilt-android-compiler:2.44'
}
项目级别build
plugins {
id 'com.android.application' version '8.0.0' apply false
id 'com.android.library' version '8.0.0' apply false
id 'org.jetbrains.kotlin.android' version '1.8.0' apply false
// hilt
id "com.google.dagger.hilt.android" version "2.44" apply false
}
这里需要说明一下,比较新的版本是如上这么写。
如果是比较旧的android studio,则是classpath的写法。比如:
buildscript {
dependencies {
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.44'
}
}
以上,配置完成。
四.2、汽车和引擎的例子
// 使用 @HiltAndroidApp 注解标记一个应用,这是使用 Hilt 进行依赖注入的入口点。
// Hilt 需要一个入口点来提供依赖,这通常是 AndroidApplication 类。
// 这个注解会触发 Hilt 的代码生成,生成的代码包括一个应用级别的组件,这个组件可以为其他 Hilt 组件(如 Activity 组件)提供依赖。
@HiltAndroidApp
class MyApplication : Application() {
// 这个类通常不需要写任何代码,除非你需要在应用启动时执行一些操作。
}
android:name=".MyApplication"
Engine
// 使用 @Inject 注解标记构造函数,告诉 Hilt 如何创建这个类的实例。
// 当 Hilt 需要提供一个 Engine 实例时,它会调用这个构造函数。
// 在这个例子中,Engine 类没有任何依赖,所以构造函数没有任何参数。
class Engine @Inject constructor() {
fun start() {
println("Engine started")
}
}
Car
// 使用 @Inject 注解标记构造函数,告诉 Hilt 如何创建这个类的实例。
// 当 Hilt 需要提供一个 Car 实例时,它会调用这个构造函数。
// 在这个例子中,Car 类依赖于 Engine 类,所以构造函数有一个 Engine 类型的参数。
// Hilt 会查找提供 Engine 实例的方法(在这个例子中,就是 Engine 类的构造函数),然后用提供的 Engine 实例来创建 Car 实例。
class Car @Inject constructor(private val engine: Engine) {
fun drive() {
engine.start()
println("Car is driving")
}
}
Activity
/**
*
* 使用 @AndroidEntryPoint 注解标记一个 Android 组件,告诉 Hilt 这个组件可以接收依赖注入。
* Hilt 支持多种 Android 组件,包括 Activity、Fragment、View、Service、BroadcastReceiver 等。
* 当一个组件被标记为 @AndroidEntryPoint,Hilt 会为这个组件生成一个组件类(在这个例子中,是 MainActivityComponent)。
* 这个组件类是用来提供依赖的,它是由 Hilt 自动生成的,你无需手动创建。
*/
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var car: Car
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Use the car
car.drive()
}
}
运行结果:
System.out: Engine started
System.out: Car is driving
嗯。如果只是这么用,那么确实平平无奇,仿佛是一个高级传参。 但是,如果只是这样,那我们还H个什么H。
但是,这是一个挺好的小例子。
五、实际开发中一些常用的场景
SharedPreferences的注入:
// 创建一个Hilt模块,用于提供SharedPreferences实例
@Module
@InstallIn(SingletonComponent::class)
object SharedPreferencesModule {
@Provides
fun provideSharedPreferences(@ApplicationContext context: Context): SharedPreferences {
return context.getSharedPreferences("pref_name", Context.MODE_PRIVATE)
}
}
// 在Activity中,你可以使用@Inject注解来请求一个SharedPreferences实例。
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var sharedPreferences: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 使用SharedPreferences
val editor = sharedPreferences.edit()
editor.putString("key", "value")
editor.apply()
}
}
上述代码中,@AndroidEntryPoint
是一个Hilt注解,用于指示Hilt应该为这个Activity提供依赖。然后,通过在sharedPreferences
字段上添加@Inject
注解,Hilt就会知道它需要为这个字段提供一个SharedPreferences实例。这个实例是由SharedPreferencesModule
模块中的provideSharedPreferences
方法提供的。
注意,你可以随时使用sharedPreferences
字段,Hilt会确保在onCreate
方法调用时,它已经被正确初始化。
你也可以将这种模式应用于其他的例子,例如网络服务、视图模型、数据库和数据仓库等。
多模块项目
在多模块项目中,Hilt可以帮助你更好地管理跨模块的依赖。例如,假设你有一个data
模块和一个app
模块,data
模块提供了一个Repository
类,app
模块需要使用这个Repository
。
首先,在data
模块中,你定义了一个Repository
类,并用@Inject
注解标记其构造函数:
// 在 data 模块中
class Repository @Inject constructor() {
// ...
}
然后,在app
模块中,你可以直接在需要的地方注入Repository
:
// 在 app 模块中
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject lateinit var repository: Repository
// ...
}
在这个例子中,你不需要在app
模块中手动创建Repository
的实例,Hilt会自动为你处理。
再来一个依赖管理的例子
以为简化的Retrofit为例子
假设我们有一个NetworkModule
,它提供了一个Retrofit
实例:
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Singleton
@Provides
fun provideRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl("https://api.example.com/")
.build()
}
}
在这个例子中,NetworkModule
是一个Hilt模块,它在应用级别的组件(SingletonComponent
)中提供了一个Retrofit
实例。这个Retrofit
实例是一个单例,因为我们使用了@Singleton
注解。
然后,我们有一个Repository
类,它需要这个Retrofit
实例来发起网络请求:
class Repository @Inject constructor(private val retrofit: Retrofit) {
// ...
}
在这个例子中,Repository
类通过构造函数注入获取了Retrofit
实例。我们不需要手动创建Retrofit
实例,Hilt会自动为我们处理。
最后,我们有一个ViewModel
,它需要这个Repository
来获取数据:
class MyViewModel @ViewModelInject constructor(private val repository: Repository) : ViewModel() {
// ...
}
在这个例子中,MyViewModel
通过@ViewModelInject
注解获取了Repository
实例。我们不需要手动创建Repository
实例,Hilt会自动为我们处理。
这就是一个典型的依赖链:MyViewModel
依赖于Repository
,Repository
依赖于Retrofit
。通过Hilt,我们可以轻松地管理这个依赖链,而无需手动创建和管理每个依赖。这使得代码更加清晰和直观,也使得新成员更容易理解项目的结构。
ViewModel注入
// Hilt提供了一个HiltViewModel注解,它允许你在ViewModel的构造函数中使用@Inject。
@HiltViewModel
class MyViewModel @Inject constructor(private val userRepository: UserRepository) : ViewModel() {
// ...
}
// 在Activity或Fragment中,你可以使用由Hilt提供的ViewModel实例。
@AndroidEntryPoint
class MyActivity : AppCompatActivity() {
private val viewModel: MyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Use viewModel here
}
}
数据库的注入:
// 创建一个Hilt模块,用于提供Room数据库实例
@Module
@InstallIn(SingletonComponent::class)
object DatabaseModule {
@Provides
fun provideDatabase(@ApplicationContext context: Context): MyDatabase {
return Room.databaseBuilder(
context,
MyDatabase::class.java, "database-name"
).build()
}
@Provides
fun provideUserDao(database: MyDatabase): UserDao {
return database.userDao()
}
}
// 在需要UserDao的地方,使用@Inject注解来请求一个UserDao实例。
class UserRepository @Inject constructor(private val userDao: UserDao) {
// ...
}
不是每一个Activity都需要依赖注入,如果这个activity只在当前页面使用,那么没必要依赖注入。
Hilt篇,大概就到这里吧。