作者:J船长
一、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与这些服务紧密耦合,难以进行单元测试。通过使用依赖注入,我们可以在一个统一的地方配置这些服务,然后在需要的地方自动注入,这使得代码更加清晰,更容易测试。
-
主App和Module:在一个模块化的应用中,不同的模块可能需要访问一些共享的服务。如果没有依赖注入,我们可能需要通过一些复杂的方式来让这些模块获取到这些服务,这会使得代码变得复杂,而且难以管理。通过使用依赖注入,我们可以在主App中配置这些服务,然后在各个模块中自动注入,这使得代码更加清晰,更容易管理。
-
管理单例依赖项:在 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能够自动处理依赖的创建和注入,大大简化了依赖注入的过程。
@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只在当前页面使用,那么没必要依赖注入。
Android 学习笔录
Android 性能优化篇:https://qr18.cn/FVlo89
Android 车载篇:https://qr18.cn/F05ZCM
Android 逆向安全学习笔记:https://qr18.cn/CQ5TcL
Android Framework底层原理篇:https://qr18.cn/AQpN4J
Android 音视频篇:https://qr18.cn/Ei3VPD
Jetpack全家桶篇(内含Compose):https://qr18.cn/A0gajp
Kotlin 篇:https://qr18.cn/CdjtAF
Gradle 篇:https://qr18.cn/DzrmMB
OkHttp 源码解析笔记:https://qr18.cn/Cw0pBD
Flutter 篇:https://qr18.cn/DIvKma
Android 八大知识体:https://qr18.cn/CyxarU
Android 核心笔记:https://qr21.cn/CaZQLo
Android 往年面试题锦:https://qr18.cn/CKV8OZ
2023年最新Android 面试题集https://qr18.cn/CgxrRy
Android 车载开发岗位面试习题:https://qr18.cn/FTlyCJ
音视频面试题锦:https://qr18.cn/AcV6Ap