美文网首页Android开发
Dagger2、Hilt学习笔记

Dagger2、Hilt学习笔记

作者: Android小悟空 | 来源:发表于2021-03-04 19:52 被阅读0次

    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

    相关文章

      网友评论

        本文标题:Dagger2、Hilt学习笔记

        本文链接:https://www.haomeiwen.com/subject/zwokqltx.html