一身诺、Hilt 干依賴注入的
Hilt是干嘛的
Hilt蜜托,一個依賴注入框架,谷歌搞出來的霉赡,基于Dagger橄务,但是更加簡潔易用。
什么是依賴注入 (Dependency Injection)
依賴注入是一種設計模式穴亏。主要就是解耦蜂挪!解耦!解耦嗓化!棠涮。
依賴注入是一種設計模式,它通過在對象之間傳遞依賴關系刺覆,使得對象能夠更靈活严肪、可測試和可維護。在Android開發(fā)中谦屑,依賴注入可以幫助我們管理和解耦應用程序中的各種組件,如Activity、Fragment很洋、ViewModel喉磁、Repository等。
既生Dagger 何生 Hilt 涝焙?
安卓中的Hilt是一種用于依賴注入(Dependency Injection)的庫仑撞。Hilt是由Google開發(fā)的妖滔,它是基于Dagger的一個擴展庫座舍,旨在簡化Android應用程序中的依賴注入過程。
Hilt提供了一套注解和生成代碼采蚀,用于在Android應用程序中自動完成依賴注入的配置。它簡化了Dagger的使用纲爸,減少了樣板代碼的編寫缩焦,提高了開發(fā)效率责静。
Hilt還提供了一些特定于Android的功能灾螃,如對Android組件的注入支持
,以及在不同生命周期中管理依賴關系的能力
嵌赠。
Hilt 大概怎么用
- Application 注解下
- 在需要的標記為依賴注入的Activity姜挺、Fragment注解下
@AndroidEntryPoint
使用Hilt進行依賴注入時炊豪,首先需要在應用程序的Application類上添加@HiltAndroidApp
注解拧篮,以告知Hilt該應用程序將使用Hilt進行依賴注入串绩。然后,可以在其他組件(如Activity高氮、Fragment)中使用@AndroidEntryPoint
注解來標記需要進行依賴注入的地方纫溃。Hilt會根據注解生成必要的代碼韧掩,在運行時完成依賴注入。
Hilt還提供了一些其他的注解费彼,用于標記和配置依賴關系口芍。例如鬓椭,
- 可以使用
@Inject
注解來標記需要注入的依賴項, - 使用
@Module
注解來標記用于提供依賴項的模塊類翘瓮, - 使用
@Provides
注解來標記提供依賴項的方法等资盅。
二踊赠、什么是TM的依賴注入筐带,給個例子
比如我們創(chuàng)建一輛汽車,汽車需要引擎荣赶。
- 如果
不用依賴注入
,創(chuàng)建汽車利诺,汽車內部還要創(chuàng)建引擎
慢逾,這樣就耦合 耦合 偶爾
- 如果
使用依賴注入
,創(chuàng)建汽車不需要創(chuàng)建引擎
口注,而是從外部將Engine
對象傳遞
給Car
類寝志,這樣就解耦 解耦 解耦
不使用依賴注入,汽車內部創(chuàng)建引擎毫缆,耦合耦合耦合乐导,
class Engine {
void start() {
System.out.println("Engine started");
}
}
class Car {
private Engine engine;
Car() {
engine = new Engine(); // Car類自己創(chuàng)建Engine對象
}
void drive() {
engine.start();
System.out.println("Car is driving");
}
}
使用依賴注入物臂,傳遞引擎對象給汽車,汽車內部不創(chuàng)建引擎對象蛾狗,解耦解耦解耦
class Car {
private Engine engine;
Car(Engine engine) { // Engine對象通過構造函數(shù)注入
this.engine = engine;
}
void drive() {
engine.start();
System.out.println("Car is driving");
}
}
現(xiàn)在淘太,我們可以在創(chuàng)建Car
對象時蒲牧,把一個Engine
對象傳遞給它:
Engine engine = new Engine();
Car car = new Car(engine);
car.drive();
上面的對比赌莺。雖然簡單艘狭,但是是個挺好的例子
"Engine對象通過構造函數(shù)注入"是指我們通過Car
類的構造函數(shù)將Engine
對象傳遞給Car
類。這是一種依賴注入的方式遵倦,被稱為構造函數(shù)注入梧躺。
在這個例子中傲绣,Car
類需要一個Engine
對象來工作秃诵。在沒有使用依賴注入的情況下,Car
類會自己創(chuàng)建一個Engine
對象禁舷。但這樣做的問題是,Car
類與Engine
類緊密耦合在一起在讶,如果我們想要更換一個不同類型的Engine
构哺,或者我們想要在測試時使用一個模擬的Engine
战坤,我們就需要修改Car
類的代碼途茫。
當我們使用構造函數(shù)注入時,我們不再在Car
類內部創(chuàng)建Engine
對象娜扇,而是在創(chuàng)建Car
對象時雀瓢,從外部將Engine
對象傳遞給Car
類刃麸。這樣司浪,Car
類就不再依賴于Engine
類的具體實現(xiàn)啊易,我們可以很容易地更換不同類型的Engine
,或者在測試時使用一個模擬的Engine
箱蝠,而不需要修改Car
類的代碼。
也許你會說牙瓢,不就是傳個參嗎矾克,說得這么麻煩?酒繁?州袒?? 某種程度上他匪,你可以這么說邦蜜,當時亥至,這個玩意姐扮,能放大了玩。
依賴注入杯缺,可不僅僅是高級的傳參 (李姐會理解)
人家萍肆,可傳參胀屿,牛逼多了宿崭。
-
Activity和Fragment:在Android開發(fā)中,Activity和Fragment經常需要訪問一些共享的資源或服務奖蔓,例如
網絡請求Retrofit
吆鹤、數(shù)據庫訪問疑务、ViewModel等。如果沒有依賴注入知允,我們可能需要在每個Activity或Fragment中手動創(chuàng)建這些對象温鸽,這會導致代碼重復,而且使得Activity和Fragment與這些服務緊密耦合配椭,難以進行單元測試股缸。通過使用依賴注入敦姻,我們可以在一個統(tǒng)一的地方配置這些服務歧杏,然后在需要的地方自動注入,這使得代碼更加清晰旺入,更容易測試凯力。
-
Activity和Fragment:在Android開發(fā)中,Activity和Fragment經常需要訪問一些共享的資源或服務奖蔓,例如
- 主App和Module:在一個模塊化的應用中,不同的模塊可能需要訪問一些共享的服務拗秘。如果沒有依賴注入祈惶,我們可能需要通過一些復雜的方式來讓這些模塊獲取到這些服務捧请,這會使得代碼變得復雜疹蛉,而且難以管理。通過使用依賴注入讹蘑,我們可以在主App中配置這些服務座慰,然后在各個模塊中自動注入版仔,這使得代碼更加清晰误墓,更容易管理谜慌。
- 3、管理單例依賴項:在 Android 應用程序中变泄,有一些依賴項是單例的妨蛹,如數(shù)據庫蛙卤、網絡客戶端等噩死。使用 Hilt 可以更輕松地管理這些單例依賴項甜滨,同時避免了手動管理單例依賴項的復雜性衣摩。
這個說是不是不會覺得是簡答傳參了!
依賴注入使得我們的代碼更加模塊化既琴,每個類都只關注自己的職責甫恩,不需要關心其依賴對象的創(chuàng)建和管理磺箕。這使得我們的代碼更容易重用。
三简僧、Hilt的一些常見注解
在開始app例子之前雕欺,還需要需要將一些注解前面說明一下岛马。
Hilt使用了一系列的注解來簡化依賴注入的過程啦逆。以下是一些最常用的Hilt注解:
@HiltAndroidApp
-
@HiltAndroidApp
: 這個注解用于Application類夏志,它會觸發(fā)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
: 這個注解用于字段备韧、構造函數(shù)或方法,告訴Hilt需要注入依賴叠艳。對于字段附较,Hilt會自動注入相應的依賴翅睛;對于構造函數(shù)捕发,Hilt會使用它來創(chuàng)建類的實例扎酷;對于方法遏匆,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
注解的構造函數(shù)厕诡,告訴Hilt提供的依賴是單例的营勤。
@Singleton
class SomeSingletonClass @Inject constructor()
@ViewModelInject
-
@ViewModelInject
: 這個注解用于ViewModel的構造函數(shù)葛作,告訴Hilt如何創(chuàng)建ViewModel的實例。
class MyViewModel @ViewModelInject constructor(private val repository: Repository) : ViewModel()
@Assisted
- @Assisted:用于標記 ViewModel 的構造函數(shù)參數(shù)绪穆,以便在使用 assisted injection 時注入這些參數(shù)玖院。
@AssistedInject
- @AssistedInject:用于標記使用 assisted injection 創(chuàng)建的 ViewModel 的構造函數(shù)难菌。
這些注解使得Hilt能夠自動處理依賴的創(chuàng)建和注入扔傅,大大簡化了依賴注入的過程猎塞。
注解 | 描述 |
---|---|
@HiltAndroidApp |
用于Application類荠耽,觸發(fā)Hilt的代碼生成铝量,包括一個應用級別的組件慢叨,這個組件可以為其他Hilt組件(如Activity組件)提供依賴拍谐。 |
@AndroidEntryPoint |
用于Android組件轩拨,如Activity亡蓉、Fragment、Service等淋肾,告訴Hilt這些組件可以接收依賴注入巫员。 |
@Inject |
用于字段 、構造函數(shù) 或方法 感猛,告訴Hilt需要注入依賴奢赂。對于字段咱士,Hilt會自動注入相應的依賴 序厉;對于構造函數(shù)弛房,Hilt會使用它來創(chuàng)建類的實例 文捶;對于方法粹排,Hilt會在注入后調用它 顽耳。 |
@Module |
用于對象斧抱,這些對象提供了一系列的依賴提供方法弄抬。這些方法用@Provides 注解標記掂恕,Hilt會在需要時調用它們弛槐。 |
@Provides |
用于在@Module 注解的類中的方法苟弛,這些方法提供了依賴的實例。Hilt會在需要時調用這些方法忿项。 |
@InstallIn |
用于@Module 注解的類钧唐,指定這個模塊安裝在哪個Hilt組件中。 |
@Singleton |
用于@Provides 注解的方法或@Inject 注解的構造函數(shù)匠襟,告訴Hilt提供的依賴是單例的钝侠。 |
@ViewModelInject |
用于ViewModel的構造函數(shù),告訴Hilt如何創(chuàng)建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 類咧叭。
// 這個注解會觸發(fā) Hilt 的代碼生成颖变,生成的代碼包括一個應用級別的組件,這個組件可以為其他 Hilt 組件(如 Activity 組件)提供依賴佩脊。
@HiltAndroidApp
class MyApplication : Application() {
// 這個類通常不需要寫任何代碼,除非你需要在應用啟動時執(zhí)行一些操作。
}
android:name=".MyApplication"
Engine
// 使用 @Inject 注解標記構造函數(shù)慨代,告訴 Hilt 如何創(chuàng)建這個類的實例。
// 當 Hilt 需要提供一個 Engine 實例時妇汗,它會調用這個構造函數(shù)告唆。
// 在這個例子中,Engine 類沒有任何依賴,所以構造函數(shù)沒有任何參數(shù)畜侦。
class Engine @Inject constructor() {
fun start() {
println("Engine started")
}
}
Car
// 使用 @Inject 注解標記構造函數(shù)验懊,告訴 Hilt 如何創(chuàng)建這個類的實例召烂。
// 當 Hilt 需要提供一個 Car 實例時,它會調用這個構造函數(shù)漫谷。
// 在這個例子中,Car 類依賴于 Engine 類俺祠,所以構造函數(shù)有一個 Engine 類型的參數(shù)肺然。
// Hilt 會查找提供 Engine 實例的方法(在這個例子中吐葱,就是 Engine 類的構造函數(shù)),然后用提供的 Engine 實例來創(chuàng)建 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 自動生成的,你無需手動創(chuàng)建喜德。
*/
@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蝙云。
但是廷区,這是一個挺好的小例子。
五锋勺、實際開發(fā)中一些常用的場景
SharedPreferences的注入:
// 創(chuàng)建一個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
方法調用時秘案,它已經被正確初始化。
你也可以將這種模式應用于其他的例子,例如網絡服務裕膀、視圖模型访忿、數(shù)據庫和數(shù)據倉庫等。
多模塊項目
在多模塊項目中,Hilt可以幫助你更好地管理跨模塊的依賴刺啦。例如留特,假設你有一個data
模塊和一個app
模塊,data
模塊提供了一個Repository
類玛瘸,app
模塊需要使用這個Repository
蜕青。
首先,在data
模塊中糊渊,你定義了一個Repository
類市咆,并用@Inject
注解標記其構造函數(shù):
// 在 data 模塊中
class Repository @Inject constructor() {
// ...
}
然后,在app
模塊中再来,你可以直接在需要的地方注入Repository
:
// 在 app 模塊中
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject lateinit var repository: Repository
// ...
}
在這個例子中蒙兰,你不需要在app
模塊中手動創(chuàng)建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
實例來發(fā)起網絡請求:
class Repository @Inject constructor(private val retrofit: Retrofit) {
// ...
}
在這個例子中,Repository
類通過構造函數(shù)注入獲取了Retrofit
實例拢军。我們不需要手動創(chuàng)建Retrofit
實例楞陷,Hilt會自動為我們處理。
最后茉唉,我們有一個ViewModel
固蛾,它需要這個Repository
來獲取數(shù)據:
class MyViewModel @ViewModelInject constructor(private val repository: Repository) : ViewModel() {
// ...
}
在這個例子中,MyViewModel
通過@ViewModelInject
注解獲取了Repository
實例度陆。我們不需要手動創(chuàng)建Repository
實例艾凯,Hilt會自動為我們處理。
這就是一個典型的依賴鏈:MyViewModel
依賴于Repository
懂傀,Repository
依賴于Retrofit
趾诗。通過Hilt,我們可以輕松地管理這個依賴鏈蹬蚁,而無需手動創(chuàng)建和管理每個依賴恃泪。這使得代碼更加清晰和直觀,也使得新成員更容易理解項目的結構缚忧。
ViewModel注入
// Hilt提供了一個HiltViewModel注解悟泵,它允許你在ViewModel的構造函數(shù)中使用@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
}
}
數(shù)據庫的注入:
// 創(chuàng)建一個Hilt模塊,用于提供Room數(shù)據庫實例
@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篇,大概就到這里吧每强。