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