Android Hilt依賴注入框架的使用

2021-12-22 CC Tech

中國移動家庭運營中心融合通信 閆同學

一、什麼是依賴注入?

依賴注入是一個聽起來很「高大上」的概念,但是其實很簡單。本來我要接受各種參數自己構造一個對象,現在只接受一個已經實例化的對象直接作為參數。

class A(a:Int,b:Int){  val B = B(a,b)}class A(val b:B)

沒錯,依賴注入的概念就是這麼簡單。

所謂依賴注入,其實就是之前在類的內部自己構造成員變量,現在放到了外部構造成員變量。

而Hilt,其實就是Android基於Dragger開發的一套Android上的依賴注入框架而已。

二、為什麼要用依賴注入

當然是降低代碼的耦合關係了!

打個比方,我要買一把錘子,可以有以下的三種辦法

可以找生產錘子的工廠生產,向工廠購買。這對應了java中的工廠模式打電話給賣錘子的商店,讓人把錘子送貨上門,這就是依賴注入。

採用依賴注入之後,「我」不需要關心錘子是哪裡生產的,也不需要關心是錘子是怎麼生產的,我只需要拿來直接使用就好。

這樣就解除了調用者(我)和被調用者(錘子)的耦合關係。這對於一個大型項目而言,意義重大。

三、Hilt怎麼用1. gradle配置接入

在項目根目錄的build.gradle添加如下代碼

buildscript {    ...    dependencies {        ...        classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha'    }}

PS:項目根目錄加這個的意義在於「聲明」編譯過程中所需要的插件,對應後面的module中的apply plugin

在module的build.gradle中添加

...apply plugin: 'kotlin-kapt'apply plugin: 'dagger.hilt.android.plugin'
android { compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 }}
dependencies { implementation "com.google.dagger:hilt-android:2.28-alpha" kapt "com.google.dagger:hilt-android-compiler:2.28-alpha"}

然後這樣就基本配置完成,如果gradle sync有一些錯誤的話,如提示gradle版本過低之類的,按照提示升級就好。

2. application配置

在Applicaiton上面必須使用@HiltAndroidApp ,表示開啟依賴注入

@HiltAndroidAppclass ExampleApplication : Application() { ... }

3. 組件及組件的生命周期

在介紹Hilt正式使用之前,需要先介紹組件的概念。

PS:這一部分有一點晦澀難懂,可以只有一個大概印象,後面用著用著就明白了。

Hilt中默認並不是所有的類都可以直接使用依賴注入的,對於每一個可以使用依賴注入的Android類,都有一個關聯的Hilt組件。每個組件負責實際的注入工作。

對應關係如下

Application對應ApplicationComponent組件ViewMode對應ActivityRetainedComponent組件Activity對應ActivityComponent組件Fragment對應FragmentComponent組件帶@WithFragmentBindings的View對應ViewWithFragmentComponent組件Service對應ServiceComponent組件

注意:Hilt不會為廣播接收器生成組件,因為Hilt直接從ApplicationComponent注入廣播接收器


組件的生命周期

各個組件都有自己的生命周期,在創建時機中自動創建注入,在銷毀時機中銷毀生成的組件實例。

各個組件的生命周期對應如下

組件創建時機銷毀時機ApplicationComponentApplication#onCreateApplication#onDestoryActivityRetainedComponentActivity#onCreateActivity#onDestoryActivityComponentActivity#onCreateActivity#onDestoryFragmentComponentFragment#onAttachFragment#onDestoryViewComponentView#super視圖銷毀時ViewWithFragmentComponentView#super視圖銷毀時ServiceComponentServer#onCreateService#onDestory


注意順便說下,ActivityRetainedComponent與ActivityComponent有什麼不同?ActivityRetainedComponent在配置更改後依然存在,如橫豎屏切換。他是在第一次onCreate的時候創建,在最後一次onDestory中銷毀,這個和ViewModel的特性保持一致。

4. Hilt的各個註解的用法

終於要講到怎麼用了

@AndroidEntryPoint表示這個類可以使用注入項。

@AndroidEntryPointclass ExampleActivity : AppCompatActivity() { ... }

目前AndroidEntryPoint可以在以下類中使用

其中,如果Fragment使用,那麼包含該Fragment的Activity也必須使用該註解,如果View使用,那麼使用該View的Fragment和Activity也必須使用該註解

@Inject該註解有兩個作用

有點繞,直接看代碼

class AnalyticsAdapter @Inject constructor() { ... }
@AndroidEntryPointclass ExampleActivity : AppCompatActivity() {
@Inject lateinit var analytics: AnalyticsAdapter ...}

那analytics是在Activity生命周期的哪一步被注入呢?可以參考組件那一章節,是在onCreate中注入的。

@ViewModelInject與Inject類似,但是專用於ViewModel的構造函數上,如

@ActivityRetainedScopedclass ZdmViewModel @ViewModelInject constructor(private val adapter:AnalyticsAdapter,@Assisted private val state:SavedStateHandle) : BaseModel(), LifecycleObserver {}

@ApplicationContext @ActivityContext直接拿到Application和Activity的Context實例,注意這裡的Context只能作為Context使用,而不是Activity實例,至於為什麼,有機會再介紹。用法如下

@Singletonclass DefaultConfigService @Inject constructor(@ApplicationContext context: Context) : BaseConfigService(context) {}

@Singleton一般情況下來說,類成員變量使用@Inject 之後,每次都new一個實例出來(ViewModel例外,具體的先不講了,以後另開文章講)。如果想使用單例模式,可以在類上使用@Singleton, 標明這個類是單例模式

@Singletonclass DefaultConfigService @Inject constructor(@ApplicationContext context: Context) : BaseConfigService(context) {}

好了,以上都是一些基礎的用法,會了這些已經會對平時的工作有幫助了,下面講一些略微高級的用法。這是一道分割線

@Moudle @InstallIn @Provider如果我想用@Inject 注入一個三方SDK的類實例,應該怎麼辦呢?

因為我們知道使用@Inject 的前提是該類的構造函數加上@Inject,所以沒有辦法直接用,那麼可以這樣。其中@Module生命模塊,@InstallIn是用於告知Hilt這個模塊將安裝在哪個Android類中(這裡我理解和生命周期息息相關)

@Module@InstallIn(ApplicationComponent::class)object AppModule {    @Singleton    @Provides    fun gson(): GsonManager {        return GsonManager.instance()    }}

@BindsBinds的作用是將接口與實現類綁定起來。這樣後面使用@Inject進行注入的時候,聲明該接口即可,而不用關心接口對應哪個類實現,進一步降低了耦合程度

@InstallIn(ApplicationComponent::class)@Moduleabstract class AppBindModule {    @Binds    abstract fun bindAccountService(defaultAccountService: DefaultAccountService): AccountService}

@EntryPoint從註解名就可以看出,@AndroidEntryPoint類似, 只不過@AndroidEntryPoint只提供了系統默認的幾個類的支持, 如果想讓自己實現的類中也可以實現註解, 可以用@EntryPoint, 只不過要稍微麻煩點, 如下。

下面這段代碼中,AccountService是採用Inject依賴注入的,但是ServicesManager因為不能用@AndroidEntryPoint來註解,所以他如果想要獲取AccountService實例,可以用下面辦法

class ServicesManager private constructor(context: Context) {    ...    @EntryPoint    @InstallIn(ApplicationComponent::class)    interface AccountServiceEntryPoint {        fun accountService(): AccountService    }    companion object {        const val ACCOUNT_SERVICE = "account"
val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { val manager = ServicesManager(LibApplication.instance()) val accountServiceEntryPoint = EntryPointAccessors.fromApplication(LibApplication.instance(), AccountServiceEntryPoint::class.java) manager.register(ACCOUNT_SERVICE, accountServiceEntryPoint.accountService()) manager } }

Hilt的使用場景

筆者能想到兩個使用場景。

工具類

毫無疑問,工具類很適合幹這個事情了。但是感覺有點多餘,工具類一般邏輯不會太複雜,使用Hilt有點牛刀殺雞的感覺。

ViewModel相關場景

這個重點介紹下。在Jetpack中,Google已經推薦使用ViewModel來管理所有的數據,加上LiveData,形成了ViewModel+LiveData+Framgent+Activity+Presenter(可選)一套非常成熟的實現思路,並且,Google在Kotlin中有兩個ViewModel的非常簡單的初始化方法

val viewModel:MyViewModel by viewModels()
val viewModel:MyViewModel by activityViewModels()

如此,Activity可以與Fragment很方便的共享數據,如果再加上LiveData,可以達到Fragment與Activity通信的目的。

在這一點上,Hilt只不過換了一種寫法而已。

@Injectlateinit var viewModel:MyViewModel

但是如果加上View呢?

有一些略微複雜的界面,有人喜歡抽成一個ViewGroup來做,這個ViewGroup只是一些View的組合而已,不需要重寫onMesure,onLayout,onDraw方法,但是因為有一些獨立的業務邏輯,所以抽成一個ViewGroup會比較方便,例如搜索控制項等。

這部分View就沒有辦法很好的與Activity或者Fragment進行通信,只能用set或者callback等命令式編程的思想來做通信,複雜且囉嗦。

這個時候就可以用到了Hilt。Hilt框架也可以在View中使用@Inject註解來獲取ViewModel。

算作一個還算不錯的實現思路。

相關焦點

  • Kotlin 中使用 Hilt 的開發實踐
    https://developer.android.google.cn/training/dependency-injection/hilt-androidhttps://developer.android.google.cn/training/dependency-injection/dagger-basics如需在應用中配置 Hilt,請先參考
  • Jetpack Hilt有哪些改善又有哪些限制?
    它在保留Dagger2的編譯時注入的性能優勢前提下,簡化了註解的使用。同時針對Android框架類進行了優化。在展開Hilt的講述之前先來簡單回顧下依賴注入的各個角色和流程。依賴注入流程依賴的需求方,通過構造參數或欄位依賴其他實例的角色,一般使用@Inject描述這種需求依賴的提供方,對被依賴的實例提供實現的角色,比如使用@Provides描述這種來源依賴的注入方,將提供方的實現注入到需求方的角色,比如使用@Component描述這種注入組件Hilt的改善定義應用組件給Application添加@HiltAndroidApp註解即可告知Hilt生成應用級別的組件
  • Hilt 擴展 | MAD Skills
    生成的代碼作為構成 Hilt 依賴項注入關係圖的模塊或入口點。Jetpack 中 WorkManager 的集成庫就是一個擴展的例子。WorkManager 擴展幫助我們減少向 worker 提供依賴項時所需的模板代碼及配置。該庫由兩部分組成,分別為 androidx.hilt:hilt-work 和 androidx.hilt:hilt-compiler。
  • Jetpack 成員 Hilt 實踐 (一) 啟程過坑記 | 開發者說·DTalk
    Hilt、Dagger、Koin 等等都是依賴注入庫,Google 也在努力不斷的完善依賴注入庫從 Dagger 到 Dagger2 再到現在的 Hilt,因為依賴注入是面向對象設計中最好的架構模式之一,使用依賴注入庫有以下優點:Hilt 是 Android 的依賴注入庫,它減少了在項目中進行手動依賴,進行手動依賴注入需要您手動構造每個類及其依賴,依賴注入庫的出現節省了 Android 開發者大量的時間
  • Android 依賴注入框架 Dagger 2.1 詳解
    本文適合人群:從未了解過dagger僅簡單使用API但不了解為何這樣使用可以很好的使用並理解dagger,僅需了解新版本(可以直接跳到第四部分)Dependency Injection(DI)翻譯成中文也就是依賴注入,我們先用一個很簡單的例子來了解一下    class Clothes {
  • Android Studio 4.1 的 Dagger 導航更新
    了解依賴項來自哪裡在一個使用 Dagger 注入的類中,如果您在依賴項上點擊帶有向上箭頭的間距圖標,您將跳轉到使用 Dagger 註解提供對應類型的方法。在下面的例子中,TasksViewModel 依賴了 TasksRepository。
  • 劃重點 | Android Jetpack 三大重要更新!
    Hilt — Jetpack 官方推薦的依賴注入開發庫Hilt 是一個新的 Android 開發庫,它可以幫助應用開發更容易地實現依賴注入。Hilt 讓您只需考慮聲明和注入綁定中的重要部分,而不必擔心管理依賴注入的初始化和連接等部分。
  • WebAPI使用依賴注入
    (點擊上方藍字,可快速關注我們)來源:神牛步行3cnblogs.com/wangrudong003/p/6253486.html本篇將要和大家分享的是webapi中如何使用依賴注入,依賴注入這個東西在接口中常用,實際工作中也用的比較頻繁,因此這裡分享兩種在api中依賴注入的方式Ninject和Unity。
  • 使用Yii2依賴注入簡化開發
    什麼是依賴注入(DI)?對象由框架來創建而不是程式設計師通過 new 創建。
  • Android開源註解注入項目介紹
    其中最常見的方式叫做依賴注入(Dependency Injection,簡稱DI),還有一種方式叫「依賴查找」(Dependency Lookup)。通過控制反轉,對象在被創建的時候,由一個調控系統內所有對象的外界實體將其所依賴的對象的引用傳遞給它。也可以說,依賴被注入到對象中。可以使用上述註解的方式實現注入。
  • 使用 Autofac 進行依賴注入
    突然有一天發現依賴注入這種技能。為了使得架構可測試、易維護、可擴展,需要架構設計為鬆耦合類型,簡單的說也就是解耦。net 的 IOC 框架有 Autofac、Castle Windsor、Unity、Spring.NET、StructureMap、Ninject。    有這麼多框架為什麼選 Autofac,我在這個地方只是學習這種 IOC 思想的框架,並沒有真實的用在生產中。
  • 開發(三) 依賴注入
    簡單來講,它是使用大型的類型解析容器來解析類的依賴的一種方式。幾乎每一種現代化的程式語言都有很多的與之對應的依賴注入框架。我們重點關注C#語言,它也有很多的依賴注入框架,像是Ninject,AutoFac,Unity( Microsoft),還有很多其他的。
  • 依賴注入和控制反轉的理解
    學習過Spring框架的人一定都會聽過Spring的IoC(控制反轉) 、DI(依賴注入)這兩個概念,對於初學Spring的人來說,總覺得IoC
  • 理解JavaScript中的依賴注入
    JavaScript也不例外,在我們使用JavaScript編寫應用時,我們是不是都會使用到別人編寫的代碼,例如一些著名的開源庫或者框架。隨著我們項目的增長,我們需要依賴的模塊變得越來越多,這個時候,如何有效的組織這些模塊就成了一個非常重要的問題。依賴注入解決的正是如何有效組織代碼依賴模塊的問題。
  • XUnit 依賴注入
    Intro現在的開發中越來越看重依賴注入的思想,微軟的 Asp.Net Core 框架更是天然集成了依賴注入,那麼在單元測試中如何使用依賴注入呢?本文主要介紹如何通過 XUnit 來實現依賴注入, XUnit 主要藉助 SharedContext 來共享一部分資源包括這些資源的創建以及釋放。
  • 依賴注入之Autofac使用總結
    今天我想結合實際項目,和正在迷惑中的新手朋友一起來學習和總結依賴注入Autofac的使用和理解。依賴注入粗暴理解依賴: public class A{ public void A(B b) { // do something } }這樣的代碼,估計沒有程序猿不曾使用。
  • 依賴注入和控制反轉
    又要名詞解釋一下,某個對象指的就是任意的、普通的Java對象; IoC/DI的容器簡單點說就是指用來實現IoC/DI功能的一個框架程序;對象的外部資源指的就是對象需要的,但是是從對象外部獲取的,都統稱資源,比如:對象需要的其它對象、或者是對象需要的文件資源等等。
  • 我曾想深入了解的:依賴倒置、控制反轉、依賴注入
    這一次,我想深入了解和探討我曾經很迷糊,也沒有一直仔細了解的:依賴倒置、控制反轉、依賴注入 這些概念。什麼是依賴?通常可以理解為一種需要,需求。需要協助才能完成一件事情。什麼是依賴注入呢?其實我們剛剛已經實現過了,全文先是依賴倒置,然後控制反轉,而現在說的依賴注入是控制反轉的具體實現方式。依賴注入是解開依賴並實現反轉的一種手段。
  • .net core 依賴注入: 什麼是控制反轉,什麼是依賴注入,什麼是容器, autofac 簡單使用
    綜述ASP.NET Core  支持依賴注入, 也推薦使用依賴注入. 主要作用是用來降低代碼之間的耦合度.
  • 詳解 Python 中的類型化函數依賴注入
    關於如何使用DI框架,有一些已知的問題、技巧乃至一整套相關的方法論。很多人問我:如何將許多類型化的函數概念與傳統的面向對象依賴注入結合使用?這個問題很有意義。因為函數式編程都是關於組合的。依賴注入只是個魔術。你會在代碼的某些地方注入一些幾乎隨機的對象,之後,被注入的整個容器在運行時由各個部分神奇地組裝而成。