移動(dòng)端開(kāi)發(fā)之Jetpack?Hilt技術(shù)實(shí)現(xiàn)解耦
Hilt是什么
Hilt 是基于 Dagger2 的針對(duì) Android場(chǎng)景定制化 的框架。
這有點(diǎn)像什么? RxAndroid 是 RxJava 的Android平臺(tái)定制化擴(kuò)展。Andorid雖然由Java、Kotlin構(gòu)成,但是它有很多平臺(tái)的特性,比如它有 Java開(kāi)發(fā) 所不知道的 Context 等。
Dagger框架雖然很出名,在國(guó)外也很流行,但是在國(guó)內(nèi)使用其的App少之又少,列舉一些缺點(diǎn):
- 上手難,眾多Android應(yīng)用框架中,Dagger必定是最難學(xué)的那一檔;
- 它就是一個(gè)復(fù)雜框架,沒(méi)有針對(duì)任何平臺(tái),所以對(duì)于所有平臺(tái)來(lái)說(shuō)會(huì)難用;
- 在Android Studio4.0版本以前,無(wú)法追蹤Dagger的依賴關(guān)系(就類比IDE無(wú)法通過(guò)快捷鍵知道一個(gè)接口有哪些實(shí)現(xiàn)類) 開(kāi)發(fā)者不知道為啥要做依賴注入
- 對(duì)于第三點(diǎn),Android Studio4.1已經(jīng)支持了該功能,但是4.1有許多Bug,很多開(kāi)發(fā)者都沒(méi)有升級(jí) 。
Hilt的出現(xiàn)解決前兩點(diǎn)問(wèn)題,因?yàn)?Hilt 是 Dagger 針對(duì)Android平臺(tái)的場(chǎng)景化框架,比如Dagger需要我們手動(dòng)聲明注入的地方,而Android聲明的地方不都在 onCreate()嗎,所以Hilt就幫我們做了,除此之外還做了很多事情,這樣一來(lái),相較于Dagger,我們能用更少代碼、更容易、更輕松的配置依賴注入。
Hilt使用地方
Google認(rèn)為移動(dòng)端應(yīng)用的架構(gòu)設(shè)計(jì),最重要的 Separation of concerns(分離關(guān)注點(diǎn))。上網(wǎng)找解釋,其實(shí)它就是 模塊解耦。下面是Google官方推薦的Android應(yīng)用架構(gòu)圖:
依賴注入(DI)概念
而 Hilt 是被定義為 依賴注入框架而被發(fā)布。什么?又是依賴注入框架?不是之前已經(jīng)有了一個(gè) Dagger2 了嗎?除了 Dagger2, 還有 ButterKnife ,Kotlin甚至還有輕量級(jí)的 Koin。
什么是依賴注入?先來(lái)看下面代碼:
class MyClass { val user = User() }
我們?cè)谝粋€(gè)類 MyClass 中 聲明了一個(gè)變量 user,并初始化-------調(diào)用 User 構(gòu)造函數(shù),創(chuàng)建一個(gè)對(duì)象。
上面這段代碼就產(chǎn)生了一個(gè)依賴關(guān)系。
我們要先看懂誰(shuí)依賴了誰(shuí)?首先 MyClass 是我們的類,User 可以是我們自己寫(xiě)的類,也可以是通過(guò)第三方Jar包或SDK里面的類。 在我們寫(xiě)的 MyClass 的代碼里面,我們需要一個(gè) User 對(duì)象來(lái)完成一些任務(wù),所以我們創(chuàng)建了 User 對(duì)象,這就說(shuō)明 MyClass 依賴了 User。對(duì)于 MyClass來(lái)說(shuō),User是外面之物,但是又需要依賴它。
如果上面這個(gè) User,不是由自己創(chuàng)建,而是由外部創(chuàng)建,然后在本類只做賦值工作 ,這個(gè)過(guò)程就是 依賴注入。
有一個(gè)我們非常熟悉的設(shè)計(jì)模式,就使用了依賴注入的方法—工廠模式:
class UserFactory { fun newUser(): User{ return User() } } class MyClass { val user = UserFactory.newUser() }
我們的 MyClass 類需要使用 User 類,但是這次沒(méi)有自己來(lái)創(chuàng)建(沒(méi)有自己new出來(lái)),而是交由給 UserFactory 來(lái)創(chuàng)建出來(lái),MyClass就做了最后的賦值工作。
對(duì)于 MyClass 來(lái)說(shuō),這就是一次依賴注入,和上面例子相比,把對(duì)象創(chuàng)建的過(guò)程交由給了別的類。
所以我們通過(guò)上面兩個(gè)例子就能知道依賴注入的本質(zhì)是什么:借由外部得到對(duì)象。依賴注入框架就是這個(gè)外部
現(xiàn)在流行的 Dagger2、Koin框架,只是讓我們更輕松、更容易的去得到對(duì)象。
Dagger的中文翻譯是 “刀”,它就像一把刀,插進(jìn)我們代碼中。那相信你也知道 ButterKnife 為什么這么取名了吧。
Hilt使用
導(dǎo)入
在 app 的 build.gradle 中加入:
plugins {
...
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
dependencies {
...
implementation 'com.google.dagger:hilt-android:2.28-alpha'
kapt 'com.google.dagger:hilt-android-compiler:2.28-alpha'
}
在 project的 build.gradle 中加入:
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha'
一個(gè)簡(jiǎn)單的例子 Hilt 需要 AndroidManifest 使用帶有 @HiltAndroidApp 注解的 Application 類,所以我們的 Application需要這樣:
@HiltAndroidApp class HiltApp : Application() { ... }
然后在 AndroidManifest 文件中聲明:
<application android:name=".HiltApp" ... </application>
假如我們要在 MainActivity 中注入一個(gè) User 對(duì)象, 我們首先編寫(xiě)一個(gè) User 類,User類有兩個(gè)屬性 name 和 age 。 誒,這個(gè)時(shí)候有同學(xué)就會(huì)問(wèn)了:我通過(guò) @Inject 聲明一個(gè)User,Hilt就能給我創(chuàng)建一個(gè) User 對(duì)象,那這個(gè)User對(duì)象里面的name和age是啥?答案是:編譯會(huì)報(bào)錯(cuò),因?yàn)槲覀冏约憾疾恢肋@兩個(gè)參數(shù)是啥,Hilt怎么可能會(huì)知道,所以這兩個(gè)屬性也要通過(guò)依賴注入的方式來(lái)注入。
為了簡(jiǎn)化這個(gè)問(wèn)題,我定義一個(gè)默認(rèn)的無(wú)參構(gòu)造函數(shù),反正創(chuàng)建之后,里面的值也是可以修改的嘛。
data class User(var name: String, var age: Int) { // 定義一個(gè)默認(rèn)的無(wú)參構(gòu)造函數(shù),并使用 @Inject 注解修飾 @Inject constructor() : this("Rikka", 23) }
接著我們?cè)?MainActivity 中聲明一個(gè) User,通過(guò) @Inject 來(lái)修飾,并且MainActivity 需要通過(guò) @AndroidEntryPoint 修飾:
// 1 @AndroidEntryPoint class MainActivity : AppCompatActivity() { // 2 @Inject lateinit var user: User override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) Log.d(TAG, "user name:${user.name} age:${user.age}") } }
Logcat 打印結(jié)果如下:
代碼解析:
注釋1: 為 MainActivity 修飾 @AndroidEntryPoint,該注解表明 該類為需要進(jìn)行依賴注入的 Android類,是Dagger針對(duì)Android場(chǎng)景化的地方。當(dāng)我們類中需要進(jìn)行依賴注入,我們?yōu)樵擃惣尤脒@個(gè)注解,它會(huì)幫助創(chuàng)建一個(gè)單獨(dú)的 Hilt組件。它不能修飾Abstract,它只能修飾:
- ComponentActivity
- (Support)Fragment
- View
- Service
- BroadcastReceiver
注釋2:我們需要注入一個(gè) User,所以我們給它加一個(gè) @Inject 注解,告訴 Hilt。因?yàn)镵otlin的語(yǔ)法問(wèn)題,這里不得不聲明 lateinit(而這里Koin的寫(xiě)法更加優(yōu)雅),接下來(lái)步驟大概是這樣的:
- Hilt 會(huì)去找User 這個(gè)類的構(gòu)造函數(shù),以此來(lái)創(chuàng)建一個(gè)對(duì)象
- Hilt 發(fā)現(xiàn) 有兩個(gè)個(gè)構(gòu)造函數(shù),而無(wú)參構(gòu)造函數(shù)被@Inject 聲明
- Hilt 會(huì)去調(diào)用被@Inject 的構(gòu)造函數(shù),創(chuàng)建一個(gè)User(“Rikka”, 23) 對(duì)象
- 返回這個(gè)對(duì)象, MainActivity 實(shí)現(xiàn)外部幫忙創(chuàng)建 User對(duì)象,實(shí)現(xiàn) User 的依賴注入。
Inject 的中文翻譯是 “注入、注射”,所以可以形象的認(rèn)為, @Inject 修飾的變量是被外界通過(guò)針筒注入進(jìn)來(lái)的。
- @Inject 可以修飾
- 構(gòu)造函數(shù) Constructors
- 變量 Fields
- 方法 Methods
構(gòu)造函數(shù)是最先被注解的,然后再是變量和方法。所以它修飾構(gòu)造函數(shù)和修飾變量,其實(shí)是不同的作用。但為了便于理解,我們可以把它看成是一個(gè)插眼工具,便于Hilt去尋找要注入的地方。
我們上面的 User 類是無(wú)參構(gòu)造函數(shù),這次假設(shè)我們要有參數(shù)的呢?其實(shí)就是參數(shù)也要注入嘛,這就是套娃來(lái)的,來(lái)看看我們給User新增一個(gè) 屬性:Clothes:
class Clothes @Inject constructor() { } class User @Inject constructor(var clothes: Clothes){ } @AndroidEntryPoint class MainActivity : AppCompatActivity() { ... Log.d(TAG, "user clothes:${user.clothes}") }
打印結(jié)果:
PS:大家不要太拘泥于有參構(gòu)造函數(shù)的創(chuàng)建,我認(rèn)為注入的作用是創(chuàng)建出一個(gè)對(duì)象,這個(gè)對(duì)象里面的內(nèi)容可以后續(xù)再傳入,它更多的體現(xiàn)、或者我們需要注意的是 “分離關(guān)注點(diǎn)”
實(shí)現(xiàn)接口實(shí)例注入
因?yàn)榻涌跊](méi)有構(gòu)造函數(shù),所以當(dāng)我們想要依賴一些接口時(shí),該怎么辦。
我們來(lái)下面的示例,我們寫(xiě)一個(gè) Profession 接口,代表職業(yè):
interface Profession { fun doJob() }
假設(shè)我們有兩個(gè)實(shí)現(xiàn)接口的類:醫(yī)生類和程序猿類:
class Doctor : Profession{ override fun doJob() { Log.d("Doctor", "doctor do job") } } class Programmer : Profession{ override fun doJob() { Log.d("Programmer", "programmer do job") } }
這個(gè)時(shí)候我給 User 類添加一個(gè)職業(yè)的屬性,并希望它能夠自動(dòng)注入:
class User @Inject constructor(var clothes: Clothes){ @Inject lateinit var profession: Profession }
因?yàn)?Profession 是一個(gè)接口,它有兩個(gè)實(shí)現(xiàn)類,所以這樣 Hilt 并不能知道我們要實(shí)例化哪個(gè)具體的實(shí)現(xiàn)類,所以編譯的時(shí)候就會(huì)報(bào)錯(cuò)。
而 Hilt 也解決這種問(wèn)題,首先我們要在每個(gè)實(shí)現(xiàn)類上注入構(gòu)造函數(shù):
class Doctor @Inject constructor() : Profession{ ... } class Programmer @Inject constructor() : Profession{ ... }
接著我們需要實(shí)現(xiàn)一個(gè)和該接口有關(guān)的 XXXModule類,它被 @Module 修飾,這個(gè)和 Dagger 中的一樣,代表它會(huì)為接口提供一個(gè)創(chuàng)建實(shí)例的工廠,同時(shí)需要加上 @InstallIn 注解,用來(lái)聲明它是被安裝到哪個(gè)組件中,該注解后面會(huì)說(shuō)到。 代碼如下:
@Module @InstallIn(ActivityComponent::class) abstract class ProfessionModule { // 1 // 2 @Binds abstract fun bindDoctor(doctor: Doctor): Profession }
注釋1: 我們寫(xiě)出來(lái)的類是一個(gè)抽象類,因?yàn)槲覀儾恍枰唧w的實(shí)現(xiàn)它,而且它沒(méi)有具體的命名規(guī)則,因?yàn)槲覀円膊粫?huì)在代碼中直接調(diào)用它,但是為了便于理解,我們起名一般叫 接口名 + Module。
注釋2: 我們假設(shè)該Module提供一個(gè) Doctor 的職業(yè),那我們需要定義一個(gè) 抽象方法 來(lái)獲取一個(gè)Doctor類。 并且 該方法需要被 @Binds 注解修飾。這樣就能被 Hilt 識(shí)別。
這樣一來(lái),我們就實(shí)現(xiàn)了接口的一個(gè)實(shí)例化的注入,我們來(lái)實(shí)驗(yàn)一下,在 User 中去展示它:
class User @Inject constructor(var clothes: Clothes){ @Inject lateinit var profession: Profession fun showMyself() { profession.doJob() } } // MainActivity @AndroidEntryPoint class MainActivity : AppCompatActivity() { @Inject lateinit var user: User override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) user.showMyself() } }
打印結(jié)果為:
可以看到我們的 Doctor 成功的注入了。
OK,我們了解了接口某一個(gè)實(shí)現(xiàn)類的注入 (Doctor),那假設(shè)這個(gè)時(shí)候,另外一個(gè)類需要注入接口的另一個(gè)實(shí)現(xiàn)類 Programmer,那我們是不是也得按照同樣的做法,在 Module類中添加呢?
@Module @InstallIn(ActivityComponent::class) abstract class ProfessionModule { @Binds abstract fun bindDoctor(doctor: Doctor): Profession @Binds abstract fun bindProgrammer(programmer: Programmer): Profession }
這個(gè)時(shí)候發(fā)現(xiàn)運(yùn)行,編譯也會(huì)報(bào)錯(cuò):
提示我們被綁定多次了。
這是因?yàn)?Doctor 和 Programmer 都是相同類型,當(dāng)他們一起被 Binds 注解,那 Hilt 不知道要去綁定哪一個(gè)。
這個(gè)時(shí)候就需要使用 @Qualifier 注解來(lái)幫助我們了,它就是為了這種 相同類型 依賴注入而產(chǎn)生的:
@Qualifier @Retention(AnnotationRetention.BINARY) annotation class BindDoctor @Qualifier @Retention(AnnotationRetention.BINARY) annotation class
我們創(chuàng)建了新的注解 BindDoctor 和 BindProgrammer,他們都被 @Qualifier 修飾,表示他們用來(lái)作用在同種類型上, @Retention 選擇使用 BINARY類型,表明該注解保留到編譯后,但無(wú)法通過(guò)反射來(lái)得到,是比較適合的注解。
接下來(lái),我們要將這些注解作用在被 Binds 注解的抽象方法上:
@Module @InstallIn(ActivityComponent::class) abstract class ProfessionModule { @BindDoctor @Binds abstract fun bindDoctor(doctor: Doctor): Profession @BindProgrammer @Binds abstract fun bindProgrammer(programmer: Programmer): Profession }
最后,在 User 中聲明使用哪一個(gè)類型的注入:
class User @Inject constructor(var clothes: Clothes){ @BindProgrammer // 這次注入一個(gè) Programmer @Inject lateinit var profession: Profession fun showMyself() { profession.doJob() } }
打印結(jié)果如下所示:
這下我們就實(shí)現(xiàn)了具體某個(gè)實(shí)例的注入啦。
實(shí)現(xiàn)第三方依賴注入
假設(shè)一些類不是由我們自己寫(xiě)的,而是由第三方庫(kù)導(dǎo)入的。比如 OkHttp ,我們?cè)谑褂镁W(wǎng)絡(luò)請(qǐng)求的時(shí)候,需要使用它,為了分離關(guān)注點(diǎn),我們需要對(duì)他進(jìn)行依賴注入。
但是 OkHttp 是我們不能修改的類,所以我們不能在它的構(gòu)造函數(shù)上加入 @Inject, 這個(gè)時(shí)候該怎么辦呢?
Dagger 中也有類似的場(chǎng)景,我們需要 @Providers 來(lái)幫助我們。除此之外,我們也需要 @Module 注解來(lái)聲明一個(gè) Module 類, 基于上面的例子,我們可以把這種 Module 類看成是一個(gè)工廠:
@Module @InstallIn(ApplicationComponent::class) class NetModule { // 1 // 2 @Provides fun provideOkHttpClient(): OkHttpClient { // 3 return OkHttpClient.Builder().build() } }
注釋1: 聲明一個(gè) NetModule,提供網(wǎng)絡(luò)庫(kù)相關(guān)的組件,并沒(méi)有和上面例子一樣聲明為抽象函數(shù),這是因?yàn)槔锩娴亩加芯唧w的實(shí)現(xiàn)方法。
注釋2: 編寫(xiě)一個(gè) provideOkHttpClient() 方法,返回一個(gè) OkHttpClient對(duì)象。 聲明一個(gè) @Providers 注解,表示這個(gè)提供的依賴對(duì)象,是第三方的類或者系統(tǒng)類,我們因?yàn)椴荒苤苯痈钠錁?gòu)造函數(shù),所以得加上這個(gè)注解。
注釋3:new 一個(gè)對(duì)象,并返回。
這樣,我們就能在我們代碼中直接使用了:
@Inject lateinit var okHttpClient:
@Providers 的本質(zhì)是什么? 第三方類因?yàn)槠渲蛔x性,Hilt不能找到其構(gòu)造函數(shù),所以需要我們自己手動(dòng)的創(chuàng)建,創(chuàng)建的方法被 @Providers 修飾, Hilt 找到這個(gè)方法,并提供由我們手動(dòng)創(chuàng)建的對(duì)象。
所以 @Providers 的本質(zhì),是由我們自己創(chuàng)建對(duì)象, Hilt 幫我們注入。
現(xiàn)在大家都不會(huì)直接使用 OkHttp,而是使用 Retrofit,所以我們來(lái)提供一個(gè) Retrofit 把:
... @Provides fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit { return Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create()) .client(okHttpClient) .build() }
因?yàn)?Retrofit的創(chuàng)建需要依賴一個(gè) OkHttpClient 對(duì)象,所以我們需要?jiǎng)?chuàng)建一個(gè),但是我們也可以注入一個(gè),因?yàn)槲覀冎耙呀?jīng)有 provideOkHttpClient,所以它就能提供一個(gè)實(shí)例,我們不用在擔(dān)心什么了。
Hilt 的內(nèi)置組件和作用域
@InstallIn 注解
我們之前看到了 @InstallIn 這個(gè)注解,它的作用是用來(lái)表明 Module 作用的地方,它的參數(shù)時(shí) xxxComponent格式,前面xx代表作用域。
因?yàn)?Hilt 是Dagger的Android場(chǎng)景化,所以它能作用的地方和我們Android息息相關(guān),有下面幾處:
- Application ->ApplicationComponent
- ViewModel ->ActivityRetainedComponent
- Activity ->ActivityComponent
- Fragment ->FragmentComponent
- View ->ViewComponent
- Service ->ServiceComponent
- View Annotation with@WithFragmentBindings ->ViewWithFragemntComponent
除了最后一個(gè),別的作用域還是挺常見(jiàn)的。他們都要通過(guò) @InstallIn 注入。
比如我們ProfessionModule是聲明成 @InstallIn(ActivityComponent::class),這就說(shuō)明只有在 Activity 的代碼中可以使用它,而在 Service 中是不能使用的。 而 NetModule 則是聲明成 InstallIn(ApplicationComponent::class)的,這說(shuō)明在全局都可以使用它提供的 OkHttpClient 和 Retrofit 對(duì)象。
使注入對(duì)象單例
像 Retrofit 、 OkHttpClient 這樣的全局都需要使用到的對(duì)象,我們希望它的作用域是全局,并且單例的。
但是 Hilt 提供的 @Inject 對(duì)象并不是單例的,每次 注入時(shí)都會(huì)重新生成一個(gè)新的實(shí)例,這就說(shuō)明,假設(shè)我們要使用 Retrofit 來(lái)做網(wǎng)絡(luò)請(qǐng)求, @Providers 每次提供的都是不一樣的,這樣對(duì)性能來(lái)說(shuō)很不友好,而且不符合常規(guī)的邏輯設(shè)定。
按照以往,我們可以通過(guò)給 NetModule 聲明一個(gè) @Singleton 注解,來(lái)讓這個(gè)類實(shí)現(xiàn)單例,來(lái)解決這個(gè)問(wèn)題。
Hilt 也有自己的解決方案,那就是使用 @xxxScope 注解,它和上面的 xxxComponent所對(duì)應(yīng),表示 在這個(gè)作用域內(nèi)單例,來(lái)看看對(duì)應(yīng)關(guān)系:
- Application ->ApplicationComponent ->@Singleton
- ViewModel ->ActivityRetainedComponent ->@ActivityRetainedScoped
- Activity ->ActivityComponent ->@ActivityScoped
- Fragment ->FragmentComponent ->@FragmentScoped
- View ->ViewComponent ->@ViewScoped
- Service ->ServiceComponent ->@ServiceScoped
- View Annotation with@WithFragmentBindings ->ViewWithFragemntComponent ->@ViewScoped
使用 @xxScoped 來(lái)替代 @InstallIn(xxxComponent::class) 聲明組件為單例。
因?yàn)?Application 是作用于全局,所以它的注解是 @Singleton,比較好理解。
作用域的包含關(guān)系
作用域也有自己的包含關(guān)系,比如被 @ActivityScoped聲明的組件,可以在 Fragment 或者 View 中使用,他們的具體包含關(guān)系如下圖所示:
Hilt 預(yù)置的 Qualifier
我介紹過(guò) Hilt 是 Dagger針對(duì)Android的場(chǎng)景化,所以它低層做了很多事情,使得在Android上更好的使用。除了上面介紹過(guò)的那些注解外,還有很多別的東西,可以讓我們?nèi)ヌ剿?,同時(shí)也了解了Dagger本身。
Context 上下文是Android 獨(dú)特的存在,它代表著 Application、Activity、Service的一些fwk層的東西。
而我們的代碼中經(jīng)常會(huì)需要 Context 來(lái)創(chuàng)建一些東西:
class A @Inject constructor(context: Context) { ... }
但是我們知道,它是系統(tǒng)類,我們無(wú)法注入 Context。那我們可以通過(guò)使用 @Providers 來(lái)創(chuàng)建嗎?
@Providers fun provideContext() { ??? }
很明顯,Context 是由AMS來(lái)創(chuàng)建的,我們無(wú)法直接創(chuàng)建一個(gè)上下文出來(lái)。這個(gè)問(wèn)題該如何解決呢?
答案是:我們不用解決,Hilt 為我們提供了它自己預(yù)置的注解 @ApplicationContext 和 @ActivityContext,我們直接使用,Hilt會(huì)幫我們注入上下文。
class A @Inject constructor(@ApplicaitonContext context: Context)
而現(xiàn)在沒(méi)有 ServiceContext,可能是用的比較少吧?
@ApplicationContext 提供的類型是 Application, 而不是我們自己的 App 自定義的 Application,加入我們要使用自己的該怎么辦呢?答案是也很簡(jiǎn)單:
@Providers fun provideApplicaiton(application: Application): MyApplication { return applicaiton as MyApplication }
直接在 Module 中提供一個(gè),并強(qiáng)轉(zhuǎn)就 OK啦。
注意
ApplicationContext 的作用域是全局, 所以它修飾的類的作用只能是 @InstallIn(Applicaiton) 或 @Singleton,其他的也同理。
之前沒(méi)有用過(guò) Dagger,因?yàn)轫?xiàng)目不需要,且難學(xué),問(wèn)題多。 Hilt 出來(lái)之后解決了大部分的痛點(diǎn),再不上車(chē)屬實(shí)就有點(diǎn)說(shuō)不過(guò)去了。
Hilt 相較與 Dagger,肯定是更好用,更適合Android來(lái)使用。 它和 Koin的比較,只是性能上的差異,網(wǎng)上大部分的文章都認(rèn)為 Hilt 性能更優(yōu),但是代碼量更多,在大的項(xiàng)目使用 Hilt 會(huì)更好,而小的項(xiàng)目?jī)烧卟顒e不會(huì)太大。具體還請(qǐng)開(kāi)發(fā)者自己研究。
Hilt 作用是 提供依賴注入,幫助程序分離關(guān)注點(diǎn),幫助搭建低耦合高內(nèi)聚的框架,學(xué)習(xí)它,有利于我們學(xué)習(xí) Android應(yīng)用架構(gòu) 方面的技能。
到此這篇關(guān)于移動(dòng)端開(kāi)發(fā)之Jetpack Hilt技術(shù)實(shí)現(xiàn)解耦的文章就介紹到這了,更多相關(guān)Jetpack Hilt解耦內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android實(shí)現(xiàn)文字翻轉(zhuǎn)動(dòng)畫(huà)的效果
本文實(shí)現(xiàn)了Android程序文字翻轉(zhuǎn)動(dòng)畫(huà)的實(shí)現(xiàn),具有一定的參考價(jià)值,有需要的朋友可以了解一下。2016-10-10Android自定義PopupWindow簡(jiǎn)單小例子
這篇文章主要為大家詳細(xì)介紹了Android自定義PopupWindow簡(jiǎn)單小例子,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11Android開(kāi)發(fā)Activity的生命周期詳解
這篇文章主要介紹了Android開(kāi)發(fā)Activity的生命周期詳解,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參一下2022-07-07Qt for Android開(kāi)發(fā)實(shí)例教程
這篇文章主要介紹了Qt for Android開(kāi)發(fā)的方法,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-08-08Android網(wǎng)絡(luò)開(kāi)發(fā)中GET與POST請(qǐng)求詳解
這篇文章主要介紹了android實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求的get和post請(qǐng)求的簡(jiǎn)單封裝與使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧<BR>2022-12-12viewPager+fragment刷新緩存fragment的方法
這篇文章主要介紹了viewPager+fragment刷新緩存fragment的方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-03-03詳解Android Studio中Git的配置及協(xié)同開(kāi)發(fā)
這篇文章主要介紹了詳解Android Studio中Git的配置及協(xié)同開(kāi)發(fā),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03Android開(kāi)發(fā)之使用SQLite存儲(chǔ)數(shù)據(jù)的方法分析
這篇文章主要介紹了Android開(kāi)發(fā)之使用SQLite存儲(chǔ)數(shù)據(jù)的方法,結(jié)合實(shí)例形式分析了Android使用SQLite數(shù)據(jù)庫(kù)實(shí)現(xiàn)針對(duì)數(shù)據(jù)的增刪改查操作相關(guān)技巧,需要的朋友可以參考下2017-07-07