Dagger2、Hilt學(xué)習(xí)筆記

Dagger2

依賴庫

compile 'com.google.dagger:dagger:2.11'
annotationProcessor 'com.google.dagger:dagger-compiler:2.11'

@Inject 注解

  • 用來標(biāo)記依賴實(shí)例,如:
class MainActivity : Activity() {
    @Inject lateinit var a:A
}

被@Inject修飾的對象的實(shí)例是通過Dagger注入到MainActivity中的,不需要手動創(chuàng)建實(shí)例
  • 用來標(biāo)記可以直接創(chuàng)建的對象實(shí)例,如:
class A @Inject constructor(){
    fun eat(){}
}

constructor()不能省略。  
被@Inject constructor()修飾的對象,在編譯階段會生成一個Factory廣場來創(chuàng)建被@Inject constructor()修飾的對象的實(shí)例

@Module和@Provides 注解

同時使用,創(chuàng)建
1. 較為復(fù)雜
2. 不屬于自己的對象
3. 通過build模式等方式創(chuàng)建的對象
的實(shí)例:

@Module
class MainModule {

    @Provides
    fun getA():A{
        return A()
    }

    @Provides
    fun getB():B{
        return B()
    }
}

創(chuàng)建@Component接口(又叫做Dagger組件)

//@Component(modules = [MainModule::class])
@Component
interface MainComponent {
    //第三步  寫一個方法 綁定Activity /Fragment
    fun injectMain(activity: MainActivity?)
}

當(dāng)使用了@Module和@Provides 注解時,要將MainModule::class放到@Component注解的參數(shù)中
當(dāng)只使用了@Inject constructor()時,可以直接使用@Component來修身Component接口
Component接口在編譯階段會自動生成一個橋梁類,來連接Activity(暫時先理解為這樣)和它對依賴對象
數(shù)據(jù)需要被注入到哪里,接口就要添加哪個類,如上的activity: MainActivity?

@Component注解中的dependencies的用法:
一個Component可以通過dependencies依賴另一個Component,可以獲取到另一個Component提供的依賴,如:
@Component(modules = PersonModule.class)
public interface PersonComponent {
    Person getPerson();
}

@Component(dependencies = PersonComponent.class)
public interface MainActivityComponent {
    void inject(MainActivity activity);
}

DaggerMainComponent.create().injectMain(this);

class MainActivity : Activity() {
    @Inject lateinit var a:A
    override fun onCreate(savedInstanceState: Bundle?) {
        // 這里通過調(diào)用DaggerMainComponent的injectMain來和DaggerMainComponent關(guān)聯(lián)上
        DaggerMainComponent.create().injectMain(this);
        // 或:
        DaggerMainComponent.builder().build().inject(this)
    }
}    

到這里,最基礎(chǔ)的Dagger的使用就學(xué)會了

@Singleton 來聲明被 創(chuàng)建的實(shí)例為全局單例

@Module
class MainModule {

    @Singleton
    @Provides
    fun getA():A{
        return A()
    }

    @Provides
    fun getB():B{
        return B()
    }
}
或者:
@Singleton
class A @Inject constructor(){
    fun eat(){
        Log.d("TBG","eat${this.toString()}")
    }
}

同時Component必須也要用@Singleton修飾,否則編譯不通過
@Singleton
//@Component(modules = [MainModule::class])
@Component
interface MainComponent {
    //第三步  寫一個方法 綁定Activity /Fragment
    fun inject(activity: MainActivity?)

    fun injectBlank(activity: BlankActivity?)
}

@Scope

用來修飾注解,可以表示注解范圍,如Singleton--全局單例

@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}

或者: 

// Creates MyCustomScope (自定義注解)
@Scope
@MustBeDocumented
@Retention(value = AnnotationRetention.RUNTIME)
annotation class MyCustomScope

通過創(chuàng)建被@Scope修飾的注解,可以來實(shí)現(xiàn)局部單例,如:

@Scope  //聲明這是一個自定義@Scope注解
@Retention(RUNTIME)
public @interface ActivityScope {
}

表示被ActivityScope修飾的注解在一個Activity中的實(shí)例是同一個實(shí)例
注意:ActivityScope、FragmentScoped等是在Hilt庫中

這里的原理可以參考:
Android 神兵利器Dagger2使用詳解(四)Scope注解的使用及源碼分析

@Qualifier 只能用來修飾注解,用來表示這是一個自定義注解

@Qualifier
@Scope
@MustBeDocumented
@Retention(value = AnnotationRetention.RUNTIME)
annotation class MyCustomScope

Hilt

Hilt 在熱門 DI 庫 Dagger 的基礎(chǔ)上構(gòu)建而成,因而能夠受益于 Dagger 的編譯時正確性、運(yùn)行時性能、可伸縮性和 Android Studio 支持
Hilt是對Dagger在Android上的一種場景化實(shí)現(xiàn)

依賴

classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha'

...
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'

android {
    ...
}

dependencies {
    implementation "com.google.dagger:hilt-android:2.28-alpha"
    kapt "com.google.dagger:hilt-android-compiler:2.28-alpha"
}

Hilt 使用 Java 8 功能。如需在項(xiàng)目中啟用 Java 8,請將以下代碼添加到 app/build.gradle 文件中:

android {
  ...
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

@HiltAndroidApp

所有使用 Hilt 的應(yīng)用都必須包含一個帶有 @HiltAndroidApp 注釋的 Application 類。

@HiltAndroidApp
class ExampleApplication : Application() { ... }

@AndroidEntryPoint

Hilt 目前支持以下 Android 類:

Application(通過使用 @HiltAndroidApp)
Activity
Fragment
View
Service
BroadcastReceiver
如果您使用 @AndroidEntryPoint 為某個 Android 類添加注釋,則還必須為依賴于該類的 Android 類添加注釋。例如,如果您為某個 Fragment 添加注釋,則還必須為使用該 Fragment 的所有 Activity 添加注釋。

注意:在 Hilt 對 Android 類的支持方面還要注意以下幾點(diǎn):
Hilt 僅支持?jǐn)U展 ComponentActivity 的 Activity,如 AppCompatActivity。
Hilt 僅支持?jǐn)U展 androidx.Fragment 的 Fragment。
Hilt 不支持保留的 Fragment。

如需從組件獲取依賴項(xiàng),請使用 @Inject 注釋執(zhí)行字段注入:

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {

  @Inject lateinit var analytics: AnalyticsAdapter
  ...
}

注意:由 Hilt 注入的字段不能為私有字段。嘗試使用 Hilt 注入私有字段會導(dǎo)致編譯錯誤。

Hilt 注入的類可以有同樣使用注入的其他基類。如果這些類是抽象類,則它們不需要 @AndroidEntryPoint 注釋。
(意思是如果抽象基類,如:BaseActivity中用@AndroidEntryPoint修飾了,則繼承自BaseActivity的Activity不再需要用@AndroidEntryPoint修飾 -- 待驗(yàn)證)

@Binds 暫未學(xué)會

后續(xù)可參考:
Inject interfaces without provide methods on Dagger 2

當(dāng)某個構(gòu)造中需要一個接口時,可以使用@Binds:

class AnalyticsAdapter @Inject constructor(
  private val service: AnalyticsService
) { ... }

假設(shè)AnalyticsService是一個接口:
interface AnalyticsService {
  fun analyticsMethods()
}

這個接口有一個實(shí)現(xiàn)類:
class AnalyticsServiceImpl @Inject constructor(
  ...
) : AnalyticsService { ... }

創(chuàng)建一個抽象的Module類,在其中增加一個被@Binds修飾的bindAnalyticsService方法,入?yún)⑹茿nalyticsServiceImpl

@Module
@InstallIn(ActivityComponent::class)
abstract class AnalyticsModule {

  @Binds
  abstract fun bindAnalyticsService(
    analyticsServiceImpl: AnalyticsServiceImpl
  ): AnalyticsService
}

待驗(yàn)證

@InstallIn(ActivityComponent::class)

與Dagger不同的是,Hilt不必手動創(chuàng)建Component,而且在Module中通過@InstallIn(ActivityComponent::class)來為我們自動創(chuàng)建Component
同時@InstallIn中可以指定Component的作用域:

  • ApplicationComponent
  • ActivityRetainedComponent
  • ActivityComponent
  • FragmentComponent
  • ServiceComponent
  • ViewComponent
    生命周期狀態(tài)圖

組件默認(rèn)綁定

每個 Hilt 組件都附帶一組默認(rèn)綁定,Hilt 可以將其作為依賴項(xiàng)注入您自己的自定義綁定。請注意,這些綁定對應(yīng)于常規(guī) Activity 和 Fragment 類型,而不對應(yīng)于任何特定子類。這是因?yàn)?,Hilt 會使用單個 Activity 組件定義來注入所有 Activity。每個 Activity 都有此組件的不同實(shí)例。

綁定關(guān)系

@ApplicationContext與@ActivityContext

Hilt內(nèi)置了@ApplicationContext與@ActivityContext可以讓我們在任意位置獲取ApplicationContext和ActivityContext

在 Hilt 不支持的類中注入依賴項(xiàng),待嘗試且沒看懂,尷尬

Hilt 支持最常見的 Android 類。不過,您可能需要在 Hilt 不支持的類中執(zhí)行字段注入。

在這些情況下,您可以使用 @EntryPoint 注釋創(chuàng)建入口點(diǎn)。入口點(diǎn)是由 Hilt 管理的代碼與并非由 Hilt 管理的代碼之間的邊界。它是代碼首次進(jìn)入 Hilt 所管理對象的圖的位置。入口點(diǎn)允許 Hilt 使用它并不管理的代碼提供依賴關(guān)系圖中的依賴項(xiàng)。

例如,Hilt 并不直接支持內(nèi)容提供程序。如果您希望內(nèi)容提供程序使用 Hilt 來獲取某些依賴項(xiàng),需要為所需的每個綁定類型定義一個帶有 @EntryPoint 注釋的接口并添加限定符。然后,添加 @InstallIn 以指定要在其中安裝入口點(diǎn)的組件,如下所示:

class ExampleContentProvider : ContentProvider() {

  @EntryPoint
  @InstallIn(ApplicationComponent::class)
  interface ExampleContentProviderEntryPoint {
    fun analyticsService(): AnalyticsService
  }

  ...
}

Hilt注入到ViewModel對象中

...
dependencies {
  ...
  implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha01'
  // When using Kotlin.
  kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha01'
  // When using Java.
  annotationProcessor 'androidx.hilt:hilt-compiler:1.0.0-alpha01'
}

在 ViewModel 對象的構(gòu)造函數(shù)中使用 @ViewModelInject 注釋來提供一個 ViewModel。您還必須使用 @Assisted 為 SavedStateHandle 依賴項(xiàng)添加注釋:

class ExampleViewModel @ViewModelInject constructor(
  private val repository: ExampleRepository,
  @Assisted private val savedStateHandle: SavedStateHandle
) : ViewModel() {
  ...
}

然后,帶有 @AndroidEntryPoint 注釋的 Activity 或 Fragment 可以使用 ViewModelProvider 或 by viewModels() KTX 擴(kuò)展照常獲取 ViewModel 實(shí)例:  

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {
  private val exampleViewModel: ExampleViewModel by viewModels()
  ...
}

Hilt注入到WorkManager

同ViewModel,參考:
使用 Hilt 注入 WorkManager

TODO 在多模塊應(yīng)用中使用 Hilt

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

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