Jetpack系列(七) — Hilt

Jetpack系列(七) — Hilt

Hilt 简单介绍

初步印象

Hilt 是专为 Android 设计的依赖项注入库,可减少在项目中手动创建实例

Hilt 通过为项目中的每个 Android 类提供容器并自动为您管理其生命周期,定义了一种在应用中执行 DI 的标准方法

Hilt 在热门 DI 库 Dagger的基础上构建而成,因而能够受益于 Dagger 提供的编译时正确性、运行时性能、可伸缩性和 Android Studio 支持。

基本概念

依赖项注入:以参数形式提供所需的对象,应用可以在构造类时提供这些依赖项,或者将这些依赖项传入需要各个依赖项的函数

  • 构造函数注入
  • 字段注入(或 setter 注入)
// 方式一: 构造函数注入
class Car(private val engine: Engine) {
    fun start() {
        engine.start()
    }
}

// 方式二: 字段注入  
class Car {
    lateinit var engine: Engine

    fun start() {
        engine.start()
    }
}

fun main(args: Array) {
    val car = Car()
    car.engine = Engine()
    car.start()
}
复制代码

依赖项容器类

  • 容器类中实现类的创建,类似于Spring框架中的效果

Hilt 目前支持以下 Android 类

  • Application(通过使用 @HiltAndroidApp
  • Activity
  • Fragment
  • View
  • Service
  • BroadcastReceiver

Hilt 基本使用

简单注入

  1. @HiltAndroidApp 注释的 Application

    // BaseApplication.kt
    @HiltAndroidApp
    class BaseApplication : Application()
    复制代码
  2. @AndroidEntryPoint 注释的其他 Android 类提供依赖项

    • 如果您使用 @AndroidEntryPoint 为某个 Android 类添加注释,则还必须为依赖于该类的 Android 类添加注释。例如,如果您为某个 Fragment 添加注释,则还必须为使用该 Fragment 的所有 Activity 添加注释。
    // MainActivity...
    @AndroidEntryPoint
    class MainActivity : AppCompatActivity() {}
    复制代码
  3. 应用和定义Hilt 绑定

    • @Inject 成员对象引入
    • @Inject 构造函数注入
    // 官方代码
    @AndroidEntryPoint
    class ExampleActivity : AppCompatActivity() {
    
      // `@Inject` 成员对象引入
      @Inject lateinit var analytics: AnalyticsAdapter
      ...
    }
    
    class AnalyticsAdapter @Inject constructor() { ... }
    复制代码

注入viewmodel

  1. 创建ViewModel

    • 我用的hilt2.35版本,直接@HiltViewModel,老版本可以使用@ViewModelInject
    @HiltViewModel
    class HomeViewModel @Inject constructor() : ViewModel() {}
    复制代码
  2. 引用ViewModel

    // HomeFragment.kt 
    private val viewModel: HomeViewModel by viewModels()
    复制代码
  3. ViewModel中有参数的情况

    • @Inject 构造函数注入
    // MainViewModel.kt
    @HiltViewModel
    class MainViewModel @Inject constructor(
       private val repository: WordRepository
    ) : ViewModel() {...}
    
    // WordRepository.kt
    class WordRepository @Inject constructor(
       private val wordDao: WordDao
    ) {...}
    
    复制代码

相关知识点

知识点一:Hilt 模块

  • 类型不能通过构造函数注入,可使用模块

  • 使用 @Binds 注入接口实例

      // 官网代码
      interface AnalyticsService {
        fun analyticsMethods()
      }
    
      // Constructor-injected, because Hilt needs to know how to
      // provide instances of AnalyticsServiceImpl, too.
      class AnalyticsServiceImpl @Inject constructor(
        ...
      ) : AnalyticsService { ... }
    
      @Module
      @InstallIn(ActivityComponent::class)
      abstract class AnalyticsModule {
    
        @Binds
        abstract fun bindAnalyticsService(
          analyticsServiceImpl: AnalyticsServiceImpl
        ): AnalyticsService
      }
    复制代码
  • 使用 @Provides 注入实例,通常是外部库、或者必须使用构建器模式创建实例

    // 官网代码
    @Module
    @InstallIn(ActivityComponent::class)
    object AnalyticsModule {
    
      @Provides
      fun provideAnalyticsService(
        // Potential dependencies of this type
      ): AnalyticsService {
          return Retrofit.Builder()
                   .baseUrl("https://example.com")
                   .build()
                   .create(AnalyticsService::class.java)
      }
    }
    复制代码

知识点二: 同一类型个绑定

  • Hilt 以依赖项的形式提供同一类型的不同实现,可以使用@Qualifier限定符为实现

    // 官方代码
    @Qualifier
    annotation class InMemoryLogger
    
    @Qualifier
    annotation class DatabaseLogger
    
    @InstallIn(SingletonComponent::class)
    @Module
    abstract class LoggingDatabaseModule {
    
        @DatabaseLogger
        @Singleton
        @Binds
        abstract fun bindDatabaseLogger(impl: LoggerLocalDataSource): LoggerDataSource
    }
    
    @InstallIn(ActivityComponent::class)
    @Module
    abstract class LoggingInMemoryModule {
    
        @InMemoryLogger
        @ActivityScoped
        @Binds
        abstract fun bindInMemoryLogger(impl: LoggerInMemoryDataSource): LoggerDataSource
    }
    复制代码

知识点三: 使用 @EntryPoint 注释创建入口点

  • 需要在 Hilt 不支持的类中执行字段注入,比如ContentProvider,可以使用 @EntryPoint 注释创建入口点

    // 官方代码
    class LogsContentProvider: ContentProvider() {
    
        @InstallIn(SingletonComponent::class)
        @EntryPoint
        interface LogsContentProviderEntryPoint {
            fun logDao(): LogDao
        }
        
        ...
        
        private fun getLogDao(appContext: Context): LogDao {
            val hiltEntryPoint = EntryPointAccessors.fromApplication(
                appContext,
                LogsContentProviderEntryPoint::class.java
            )
            return hiltEntryPoint.logDao()
        }
    }
    
    复制代码

相关链接

Jetpack系列(一) — Navigation

Jetpack系列(二) — Lifecycle

Jetpack系列(三) — LiveData

Jetpack系列(四) — ViewModel

Jetpack系列(五) — Room

Jetpack系列(六) — Paging3

参考资料

codelabs

官网

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享