Dagger2、Hilt学习笔记

Dagger2

依赖库

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

@Inject 注解

  • 用来标记依赖实例,如:
class MainActivity : Activity() {
    @Inject lateinit var a:A
}

被@Inject修饰的对象的实例是通过Dagger注入到MainActivity中的,不需要手动创建实例
  • 用来标记可以直接创建的对象实例,如:
class A @Inject constructor(){
    fun eat(){}
}

constructor()不能省略。  
被@Inject constructor()修饰的对象,在编译阶段会生成一个Factory广场来创建被@Inject constructor()修饰的对象的实例

@Module和@Provides 注解

同时使用,创建
1. 较为复杂
2. 不属于自己的对象
3. 通过build模式等方式创建的对象
的实例:

@Module
class MainModule {

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

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

创建@Component接口(又叫做Dagger组件)

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

当使用了@Module和@Provides 注解时,要将MainModule::class放到@Component注解的参数中
当只使用了@Inject constructor()时,可以直接使用@Component来修身Component接口
Component接口在编译阶段会自动生成一个桥梁类,来连接Activity(暂时先理解为这样)和它对依赖对象
数据需要被注入到哪里,接口就要添加哪个类,如上的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?) {
        // 这里通过调用DaggerMainComponent的injectMain来和DaggerMainComponent关联上
        DaggerMainComponent.create().injectMain(this);
        // 或:
        DaggerMainComponent.builder().build().inject(this)
    }
}    

到这里,最基础的Dagger的使用就学会了

@Singleton 来声明被 创建的实例为全局单例

@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

通过创建被@Scope修饰的注解,可以来实现局部单例,如:

@Scope  //声明这是一个自定义@Scope注解
@Retention(RUNTIME)
public @interface ActivityScope {
}

表示被ActivityScope修饰的注解在一个Activity中的实例是同一个实例
注意:ActivityScope、FragmentScoped等是在Hilt库中

这里的原理可以参考:
Android 神兵利器Dagger2使用详解(四)Scope注解的使用及源码分析

@Qualifier 只能用来修饰注解,用来表示这是一个自定义注解

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

Hilt

Hilt 在热门 DI 库 Dagger 的基础上构建而成,因而能够受益于 Dagger 的编译时正确性、运行时性能、可伸缩性和 Android Studio 支持
Hilt是对Dagger在Android上的一种场景化实现

依赖

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 功能。如需在项目中启用 Java 8,请将以下代码添加到 app/build.gradle 文件中:

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

@HiltAndroidApp

所有使用 Hilt 的应用都必须包含一个带有 @HiltAndroidApp 注释的 Application 类。

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

@AndroidEntryPoint

Hilt 目前支持以下 Android 类:

Application(通过使用 @HiltAndroidApp)
Activity
Fragment
View
Service
BroadcastReceiver
如果您使用 @AndroidEntryPoint 为某个 Android 类添加注释,则还必须为依赖于该类的 Android 类添加注释。例如,如果您为某个 Fragment 添加注释,则还必须为使用该 Fragment 的所有 Activity 添加注释。

注意:在 Hilt 对 Android 类的支持方面还要注意以下几点:
Hilt 仅支持扩展 ComponentActivity 的 Activity,如 AppCompatActivity。
Hilt 仅支持扩展 androidx.Fragment 的 Fragment。
Hilt 不支持保留的 Fragment。

如需从组件获取依赖项,请使用 @Inject 注释执行字段注入:

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {

  @Inject lateinit var analytics: AnalyticsAdapter
  ...
}

注意:由 Hilt 注入的字段不能为私有字段。尝试使用 Hilt 注入私有字段会导致编译错误。

Hilt 注入的类可以有同样使用注入的其他基类。如果这些类是抽象类,则它们不需要 @AndroidEntryPoint 注释。
(意思是如果抽象基类,如:BaseActivity中用@AndroidEntryPoint修饰了,则继承自BaseActivity的Activity不再需要用@AndroidEntryPoint修饰 -- 待验证)

@Binds 暂未学会

后续可参考:
Inject interfaces without provide methods on Dagger 2

当某个构造中需要一个接口时,可以使用@Binds:

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

假设AnalyticsService是一个接口:
interface AnalyticsService {
  fun analyticsMethods()
}

这个接口有一个实现类:
class AnalyticsServiceImpl @Inject constructor(
  ...
) : AnalyticsService { ... }

创建一个抽象的Module类,在其中增加一个被@Binds修饰的bindAnalyticsService方法,入参是AnalyticsServiceImpl

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

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

待验证

@InstallIn(ActivityComponent::class)

与Dagger不同的是,Hilt不必手动创建Component,而且在Module中通过@InstallIn(ActivityComponent::class)来为我们自动创建Component
同时@InstallIn中可以指定Component的作用域:

  • ApplicationComponent
  • ActivityRetainedComponent
  • ActivityComponent
  • FragmentComponent
  • ServiceComponent
  • ViewComponent
    生命周期状态图

组件默认绑定

每个 Hilt 组件都附带一组默认绑定,Hilt 可以将其作为依赖项注入您自己的自定义绑定。请注意,这些绑定对应于常规 Activity 和 Fragment 类型,而不对应于任何特定子类。这是因为,Hilt 会使用单个 Activity 组件定义来注入所有 Activity。每个 Activity 都有此组件的不同实例。

绑定关系

@ApplicationContext与@ActivityContext

Hilt内置了@ApplicationContext与@ActivityContext可以让我们在任意位置获取ApplicationContext和ActivityContext

在 Hilt 不支持的类中注入依赖项,待尝试且没看懂,尴尬

Hilt 支持最常见的 Android 类。不过,您可能需要在 Hilt 不支持的类中执行字段注入。

在这些情况下,您可以使用 @EntryPoint 注释创建入口点。入口点是由 Hilt 管理的代码与并非由 Hilt 管理的代码之间的边界。它是代码首次进入 Hilt 所管理对象的图的位置。入口点允许 Hilt 使用它并不管理的代码提供依赖关系图中的依赖项。

例如,Hilt 并不直接支持内容提供程序。如果您希望内容提供程序使用 Hilt 来获取某些依赖项,需要为所需的每个绑定类型定义一个带有 @EntryPoint 注释的接口并添加限定符。然后,添加 @InstallIn 以指定要在其中安装入口点的组件,如下所示:

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 对象的构造函数中使用 @ViewModelInject 注释来提供一个 ViewModel。您还必须使用 @Assisted 为 SavedStateHandle 依赖项添加注释:

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

然后,带有 @AndroidEntryPoint 注释的 Activity 或 Fragment 可以使用 ViewModelProvider 或 by viewModels() KTX 扩展照常获取 ViewModel 实例:  

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

Hilt注入到WorkManager

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

TODO 在多模块应用中使用 Hilt

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271