【Android架構(gòu)】依賴(lài)注入篇Dagger Hilt&Koin

什么是依賴(lài)注入?

依賴(lài)注入(Dependency Injection),在編程中被廣泛使用,非常適用于Android開(kāi)發(fā)。作為一門(mén)應(yīng)用架構(gòu)的基礎(chǔ)科學(xué),為應(yīng)用的良性發(fā)展提供了非常優(yōu)秀的支持。

實(shí)現(xiàn)依賴(lài)注入,可用為我們帶來(lái)這些好處:

  • 重用代碼
  • 易于重構(gòu)
  • 易于測(cè)試

我們都知道,在OOP開(kāi)發(fā)中,類(lèi)往往需要引用其他類(lèi)。例如,我們生產(chǎn)一個(gè)Car,總是離不開(kāi)Engine,這被成為依賴(lài)關(guān)系。那么思考一下,Car要如何獲取自己所需要的Engine呢?我們往往采用這些方式:

  1. 在類(lèi)中構(gòu)造所需依賴(lài)
  2. 從其它地方獲取
  3. 以參數(shù)方式提供

而依賴(lài)注入,就是以參數(shù)方式提供依賴(lài)的,簡(jiǎn)單來(lái)看下代碼:

class Engine {
    fun start() {
        println("Engine start!")
    }
}

// 無(wú)依賴(lài)注入
class Car {
    fun start() {
        val engine = Engine()
        engine.start()
    }
}

// 構(gòu)造函數(shù)注入
class Car(private val engine: Engine) {
    fun start() {
        engine.start()
    }
}

fun main() {
    val engine = Engine()
    val car = Car(engine)
    car.start()
}

// setter注入
class Car {
    lateinit var engine: Engine
    
    fun start() {
        engine.start()
    }
}

fun main() {
    val engine = Engine()
    val car = Car()
    car.engine = engine
    car.start()
}

什么是Dagger?

在上面,我們已經(jīng)手動(dòng)實(shí)現(xiàn)了最基礎(chǔ)的依賴(lài)注入案例。你可能會(huì)說(shuō),這么簡(jiǎn)單啊,幾行代碼而已的事嘛??扇绻@個(gè)Car示例中,慢慢的加入更多的需求,可能需要引擎、傳動(dòng)裝置、底盤(pán)以及其他部件;而要制造引擎,則需要汽缸和火花塞,依賴(lài)的類(lèi)變得越來(lái)越多,再這樣手動(dòng)實(shí)現(xiàn)依賴(lài)注入就變得非常困難。這時(shí),Dagger就該登場(chǎng)了。

Dagger通過(guò)注解的方式,在程序編譯過(guò)程中生成依賴(lài)注入所需要的靜態(tài)代碼,只要您聲明類(lèi)的依賴(lài)項(xiàng)并指定如何使用注釋滿(mǎn)足它們的依賴(lài)關(guān)系,Dagger 便會(huì)在構(gòu)建時(shí)自動(dòng)執(zhí)行以上所有操作。Dagger 生成的代碼與您手動(dòng)編寫(xiě)的代碼類(lèi)似。在內(nèi)部,Dagger 會(huì)創(chuàng)建一個(gè)對(duì)象圖,然后它可以參考該圖來(lái)找到提供類(lèi)實(shí)例的方式。對(duì)于圖中的每個(gè)類(lèi),Dagger 都會(huì)生成一個(gè) factory類(lèi),它會(huì)使用該類(lèi)在內(nèi)部獲取該類(lèi)型的實(shí)例。

Dagger簡(jiǎn)單案例

我們還是使用上面的汽車(chē)案例,用Dagger來(lái)對(duì)它進(jìn)行改造。

Inject

// Inject 告訴Dagger如何創(chuàng)建實(shí)例
class Car @Inject constructor(
    private val engine: Engine
) {
    
    fun start() {
        engine.start()
    }
}

Module

// Module 提供創(chuàng)建實(shí)例所需要的參數(shù)
@Module
class CarModule {
    
    @Provides
    fun provideEngine(): Engine {
        return Engine()
    }
}

Component

// Component 將創(chuàng)建出的實(shí)例提供給需要的地方
@Singleton
@Component(modules = [CarModule::class])
interface CarComponent {
    fun inject(activity: MainActivity)
}

Use

class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var car: Car

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 創(chuàng)建Component
        DaggerCarComponent.builder()
            .carModule(CarModule())
            .build()
            .inject(this)

        car.start()
    }
}

Hilt實(shí)踐之MVVM

清楚了Dagger最最基本的依賴(lài)注入原理之后,我們可以直接步入Google最新推出的專(zhuān)為Android而生的依賴(lài)注入庫(kù)Hilt,而不用再去糾結(jié)去Dagger在Android中配置的細(xì)枝末節(jié),極大的降低了學(xué)習(xí)成本 ,提升開(kāi)發(fā)效率。

這里直接看項(xiàng)目源碼。

Hilt示例項(xiàng)目

Kotlin依賴(lài)注入之Koin

相較于復(fù)雜的Dagger,在Kotlin中還有一個(gè)依賴(lài)注入框架koin。它運(yùn)用了大量的Kotlin特性,將依賴(lài)注入用dsl(領(lǐng)域特定語(yǔ)言)的方式實(shí)現(xiàn),使依賴(lài)注入變得異常的簡(jiǎn)單。同樣,我們準(zhǔn)備了和Hilt相同邏輯的示例項(xiàng)目。

Koin示例項(xiàng)目

總結(jié)

到這里,我們已經(jīng)介紹了Android中的依賴(lài)注入方式,相信你對(duì)依賴(lài)注入也有了一定的認(rèn)識(shí)和思考,這里我們回到開(kāi)始的地方,結(jié)合實(shí)例重新去思考我們?cè)陂_(kāi)頭提到的依賴(lài)注入帶來(lái)的三個(gè)好處,這些好處到底是如何帶來(lái)的?

  • 重用代碼:實(shí)例中我們的MainViewModel中依賴(lài)了UserRepository,我們?nèi)绻皇褂靡蕾?lài)注入,就會(huì)new一個(gè)UserRepository對(duì)象。這樣我們之后每次新建頁(yè)面的ViewModel都需要new一個(gè)UserRepository。

  • 易于重構(gòu):試想我們的App中的User信息不只是從Web獲取,還希望在本地緩存并且在網(wǎng)絡(luò)異常是從本地獲取,我們就需要在UserRepository新增構(gòu)造參數(shù)database: UserDatabase,借助于依賴(lài)注入,我們只需要重構(gòu)repository中的內(nèi)容,ViewModel中的實(shí)現(xiàn)完全不會(huì)受到任何影響。

  • 易于測(cè)試:這里設(shè)計(jì)到測(cè)試的內(nèi)容,比如我們測(cè)試UserRepository的時(shí)候,需要mock依賴(lài)的對(duì)象,如果每次都需要new一個(gè)對(duì)象,測(cè)試將會(huì)變得難以進(jìn)行。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容