一、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篇,大约就到这里吧。