Android Dagger-Hilt 依赖注入

Posted 匆忙拥挤repeat

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Dagger-Hilt 依赖注入相关的知识,希望对你有一定的参考价值。

Author: aa86799@163.com
date: 2020-09-09 00:10

文章目录

文档地址

https://developer.android.google.cn/training/dependency-injection/hilt-android

点击进入Hilt官网,了解更多细节

依赖配置

project/build.gradle

# 2021-05 hilt已发布正式版,最新版本,直接上hilt官网查看
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha'

app/build.gradle

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

android 
  ...
  compileOptions 
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  


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

@HiltAndroidApp

@HiltAndroidApp
class ExampleApplication : Application()  ... 

@HiltAndroidApp 会触发 Hilt 的代码生成操作, 生成的代码包括应用的一个基类,该基类充当应用级依赖项容器

将依赖项注入 Android 类

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity()  ... 

Hilt 目前支持以下 Android 类:

  • Application(通过使用 @HiltAndroidApp
  • Activity
  • Fragment
  • View
  • Service
  • BroadcastReceiver (直接用@AndroidEntryPoint注入不成功)
  • ContentProvider (直接用@AndroidEntryPoint注入不成功)

注意:在 Hilt 对 Android 类的支持方面还要注意以下几点:

@Inject 注入

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() 

  @Inject lateinit var analytics: AnalyticsAdapter
  ...

定义 Hilt 绑定

向 Hilt 提供绑定信息的一种方法是构造函数注入。在某个类的构造函数中使用 @Inject 注释,以告知 Hilt 如何提供该类的实例:

class AnalyticsAdapter @Inject constructor(
  @Inject lateinit var service: AnalyticsService
)  ... 

在本例中,AnalyticsServiceAnalyticsAdapter 的一个依赖项。因此,Hilt 还必须知道如何提供 AnalyticsService 的实例。

使用 @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

Hilt 模块 AnalyticsModule 带有 @InstallIn(ActivityComponent::class) 注释,因为您希望 Hilt 将该依赖项注入 ExampleActivity。此注释意味着,AnalyticsModule 中的所有依赖项都可以在应用的所有 Activity 中使用。

使用 @Provides 注入实例

如果某个类不归您所有(因为它来自外部库,如 RetrofitOkHttpClientRoom 数据库等类),或者必须使用构建器模式创建实例,也无法通过构造函数注入。

@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)
  

为同一类型提供多个绑定,使用限定符 @Qualifier

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class AuthInterceptorOkHttpClient

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class OtherInterceptorOkHttpClient

方式一

@AuthInterceptorOkHttpClient
@Provides
fun m():OkHttpClient

方式二

@Provides
fun m(@AuthInterceptorOkHttpClient client: OkHttpClient):...

方式三

class ExampleServiceImpl @Inject constructor(
  @AuthInterceptorOkHttpClient private val okHttpClient: OkHttpClient
) 

方式四

@AuthInterceptorOkHttpClient
@Inject lateinit var okHttpClient: OkHttpClient

与binds结合:(官方文档没写,猜想应该是可以的)

@Binds
@AuthInterceptorOkHttpClient
abstract fun bind(): OkHttpClient

预定义限定符:@ApplicationContext, @ActivityContext

class AnalyticsAdapter @Inject constructor(
    @ActivityContext private val context: Context,
    private val service: AnalyticsService
)  ... 

Hilt 组件

@InstallIn 注释中引用相应的组件。限定了注入的实例的生命周期范围。

Hilt 组件注入器面向的对象
ApplicationComponentApplication
ActivityRetainedComponentViewModel
ActivityComponentActivity
FragmentComponentFragment
ViewComponentView
ViewWithFragmentComponent带有 @WithFragmentBindings 注释的 View
ServiceComponentService

组件生命周期

生成的组件创建时机销毁时机
ApplicationComponentApplication#onCreate()Application#onDestroy()
ActivityRetainedComponentActivity#onCreate()Activity#onDestroy()
ActivityComponentActivity#onCreate()Activity#onDestroy()
FragmentComponentFragment#onAttach()Fragment#onDestroy()
ViewComponentView#super()视图销毁时
ViewWithFragmentComponentView#super()视图销毁时
ServiceComponentService#onCreate()Service#onDestroy()

注意ActivityRetainedComponent 在配置更改后仍然存在,因此它在第一次调用 Activity#onCreate() 时创建,在最后一次调用 Activity#onDestroy() 时销毁。

组件作用域

Android 类生成的组件作用域
ApplicationApplicationComponent@Singleton
View ModelActivityRetainedComponent@ActivityRetainedScope
ActivityActivityComponent@ActivityScoped
FragmentFragmentComponent@FragmentScoped
ViewViewComponent@ViewScoped
带有 @WithFragmentBindings 注释的 ViewViewWithFragmentComponent@ViewScoped
ServiceServiceComponent@ServiceScoped

默认情况下,Hilt 中的所有绑定都未限定作用域。这意味着,每当应用请求绑定时,Hilt 都会创建所需类型的一个新实例。

若限定作用域,如:

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

@ActivityScoped: Hilt 会在相应 Activity 的整个生命周期内提供 AnalyticsAdapter 的同一实例.

组件层次结构

父组件中绑定的依赖项,对子组件是可见的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xCeJBtzE-1604903187718)(https://developer.android.google.cn/images/training/dependency-injection/hilt-hierarchy.svg)]

注意:默认情况下,如果您在视图中执行字段注入,ViewComponent 可以使用 ActivityComponent 中定义的绑定。如果您还需要使用 FragmentComponent 中定义的绑定并且视图是 Fragment 的一部分,应将 @WithFragmentBindings 注释和 @AndroidEntryPoint 一起使用。

在 Hilt 不默认支持的类中注入依赖项

@EntryPoint 创建入口点。

ContentProvider 不能直接使用 @AndroidEntryPoint ; BroadcastReceiver 也不能。

class ExampleContentProvider : ContentProvider() 

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

  ...

实例:普通类注入 不同的Retrofit(不同的baseUrl) 对象

@EntryPoint
@InstallIn(ApplicationComponent::class)
interface FleetRetrofitProviderEntryPoint 
    @FleetRetrofit
    fun getFleetRetrofit(): Retrofit
  	
    //再定义一个不同的 Retrofit baseUrl 的 对象获取函数,不同的 Qualifier



  //
  val appContext = ExpressApplication.instance().applicationContext
                  ?: throw IllegalStateException()
  val networkModule = EntryPointAccessors.fromApplication(appContext, FleetRetrofitProviderEntryPoint::class.java)
  val retrofit = networkModule.getFleetRetrofit() //获取相应的Retrofit
  val result = retrofit.create(LoginService::class.java).m()...

详情见 fleet项目中的 NetworkModule, LoginBiz

实例:普通类 注入 Repository

@EntryPoint
@InstallIn(ApplicationComponent::class)
interface LocationReceiverEntryPoint 
    fun repository(): LocationUploadRepository



  val entry = EntryPointAccessors.fromApplication(ExpressApplication.instance().applicationContext, LocationReceiverEntryPoint::class.java)
  val result = entry.repository().updateLocation(LocationUploadBody(locationItems))

详情见 fleet项目中的 LocationUpdatesBroadcastReceiver

使用 Hilt 注入 ViewModel 对象

app/build.gradle

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。您还必须使用 @AssistedSavedStateHandle 依赖项添加注释:

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

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

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() 
  private val exampleViewModel: ExampleViewModel by viewModels() //or activityViewModels()
  ...

androidx.lifecycle.SavedStateHandle 用于 Activity/Fragment重建时保存变量用的。

  • getLiveData(key, [v]): MutableLiveData 获取不到,会内部创建 LiveData; 一个key对应一个LiveData,存放在一个 Map<String, LiveData>中。
  • keys(): Set
  • set(key, v) 从map中,若取得 LiveData,则liveData.setValue(); 若取不得则存放在 一个Map<String, Object> 中。
  • get(key) 从Map<String, Object> 中 get(key)。

使用 Hilt 注入 WorkManager

app/build.gradle

dependencies 
  ...
  implementation 'androidx.hilt:hilt-work: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'

Worker 对象的构造函数中使用 @WorkerInject 注释来注入一个 Worker。您只能在 Worker 对象中使用 @Singleton 或未限定作用域的绑定。您还必须使用 @AssistedContextWorkerParameters 依赖项添加注释:

class ExampleWorker @WorkerInject constructor(
  @Assisted appContext: Context,
  @Assisted workerParams: WorkerParameters,
  workerDependency: WorkerDependency
) : Worker(appContext, workerParams)  ... 

然后,让 Application 类实现 Configuration.Provider 接口,注入 HiltWorkFactory 的实例,并将其传入 WorkManager 配置:

@HiltAndroidApp
class ExampleApplication : Application(), Configuration.Provider 

  @Inject lateinit var workerFactory: HiltWorkerFactory

  override fun getWorkManagerConfiguration() =
      Configuration.Builder()
            .setWorkerFactory(workerFactory)
            .build()

以上是关于Android Dagger-Hilt 依赖注入的主要内容,如果未能解决你的问题,请参考以下文章

IOC 控制反转Android 视图依赖注入 ( 视图依赖注入步骤 | 视图依赖注入代码示例 )

IOC 控制反转Android 布局依赖注入 ( 布局依赖注入步骤 | 布局依赖注入代码示例 )

Java服务器端应用程序的静态依赖注入解决方案?

IOC 控制反转Android 事件依赖注入 ( 事件依赖注入代码示例 )

IOC 控制反转Android 事件依赖注入 ( 事件三要素 | 修饰注解的注解 | 事件依赖注入步骤 )

Android依赖注入框架Hilt基本使用