【Android】Hilt依赖实践记录

Hilt作为一个依赖注入的框架,能够让项目更加简洁,架构分层更加明晰,很有学习的价值。

但是,现在网上的博客有些滞后,让我们学习时候的成本有点高,因此打算重新整理一份Hilt依赖实践记录。

===

首先,整理这次实践用到的依赖

com.google.dagger:hilt-android
com.google.dagger:hilt-compiler
androidx.hilt:hilt-navigation-compose

第一个是Hilt本体,第二个是hilt使用用到的注解,第三个是在Mvvm架构通过hiltViewModel()替换viewModel()实例化ViewModel用

这里涉及到注解框架的依赖,从Java用annotationProcessor去声明注解的依赖,到Kotlin用Kapt,现在Android由提供了一个新的依赖方法ksp

与传统的Kapt相比,KSP的优势有很多,不详解,直接说明如何依赖

引入ksp

根项目 build.gradle.kts
// 这个是ksp注释 alias(libs.plugins.ksp) apply false

libs.versions.toml
```
[versions]

kotlin = “2.1.10”
ksp = “2.1.10-1.0.31”

[plugins]
...
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
```

注意,这里kotlin版本和ksp版本需要对应,如果kotlin版本过高或过低都会引起编译无法通过问题

引入hilt

根项目 build.gradle.kts
// 这个是引入 alias(libs.plugins.hilt) apply false

app下 build.gradle.kts
```
plugins {

alias(libs.plugins.hilt)
}

android {
    ...
}

dependencies {
    ...
    implementation (libs.hilt.android)
    implementation(libs.androidx.hilt.navigation.compose)
    ksp(libs.hilt.compiler)
    ...
}
```

libs.versions.toml
```
[versions]

hilt = “2.57”
hiltAndroid = “2.57”
androidxHiltNavigationCompose = “1.2.0”

[libraries]
...
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltAndroid" }
hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hiltAndroid" }
androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "androidxHiltNavigationCompose" } 

[plugins]
...
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
```

使用hilt

@HiltAndroidApp
class MyApplication : Application() {
}

@AndroidEntryPoint 现在变更为 @HiltAndroidApp

所有使用 Hilt 的应用都必须包含一个 带有. Application 注释的类 。这将启动Hilt 组件的代码生成 ,并为使用这些生成组件的应用生成一个基类。

@AndroidEntryPoint // hilt 依赖注入的注解
class HiltActivity : ComponentActivity() {

    // 无参数依赖注入
    @Inject
    lateinit var hilt1: HiltInjectClz

    // 有参数的依赖注入
    @Inject
    lateinit var hilt2: HiltInjectWithParamClz

    // 接口直接依赖注入实例
    //@Inject
    //lateinit var hilt3: HiltInjectInterface

    // 通过注解注入指定实现
    @BindH2Impl
    @Inject
    lateinit var hilt: HiltInjectInterface

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            NewPracticeApplicationTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    Greeting(
                        name = "Android",
                        modifier = Modifier
                            .padding(innerPadding)
                            .clickable {
                                hilt.print()
                            }
                    )
                }
            }
        }
    }
}

// 不带参数的依赖注入
class HiltInjectClz @Inject constructor() {

    fun print() {
        Log.e("hilt", "hilt inject success")
    }
}

/**
 * 带参数的依赖注入
 *
 * 关于预置Qualifier其实还有一个隐藏的小技巧,
 * 就是对于Application和Activity这两个类型,Hilt也是给它们预置好了注入功能。
 *
 * 也就是说,如果你的某个类依赖于Application或者Activity,不需要想办法为这两个类提供依赖注入的实例
 * 
 * 但是context还是需要 @ActivityContext 这样一个注解实现注入
 */
class HiltInjectWithParamClz @Inject constructor(
    val clz: HiltInjectClz,
    val application: Application,
    val activity: Activity,
    @ActivityContext context: Context
) {

    fun print() {
        Log.e("hilt", "hilt inject by $clz")
    }
}

/**
 * 依赖注入接口实例
 */
interface HiltInjectInterface {

    fun print()

    fun log()
}

/**
 * 两个不同的接口实现
 */
class HiltInject1Impl @Inject constructor() : HiltInjectInterface {
    override fun print() {
        Log.e("hilt", "hilt impl1 inject success")
    }

    override fun log() {
        Log.e("hilt", "hilt log impl1 inject success")
    }
}

class HiltInject2Impl @Inject constructor() : HiltInjectInterface {
    override fun print() {
        Log.e("hilt", "hilt impl2 inject success")
    }

    override fun log() {
        Log.e("hilt", "hilt log impl2 inject success")
    }
}

/**
 * 这两个注解,必须要带上 Qualifier
 * 才能被hilt识别是用来区分实例的
 */
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class BindH1Impl

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

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

    // 如果没有 BindH1Impl BindH2Impl 这两个注解,在module中只能存在一个返回接口的方法
    // 然后项目中,会返回这个方法入参中的实例
//    @Binds
//    abstract fun bindHilt1(impl: HiltInject1Impl): HiltInjectInterface

    @BindH1Impl
    @Binds
    abstract fun bindHilt1(impl: HiltInject1Impl): HiltInjectInterface

    @BindH2Impl
    @Binds
    abstract fun bindHilt2(impl: HiltInject2Impl): HiltInjectInterface
}

通过这么一个案例,演示了多种Hilt的基础使用方法,包括无参数、有参数,接口注入

@Module
@InstallIn(SingletonComponent::class)
class ApplicationModule {

    @Provides
    fun provideMyApplication(application: Application): MyApplication {
        return application as MyApplication
    }
}

这里提供通过注解拿自定义application的方法

@HiltViewModel
class HiltViewModel @Inject constructor(val repository: Repository) : ViewModel() {
}

/**
 * 仓库层
 */
class Repository @Inject constructor()


@Composable
fun Greeting(
    name: String,
    modifier: Modifier = Modifier,
    viewModel: HiltViewModel = hiltViewModel()
) {
    Text(
        text = "Hello $name!",
        modifier = modifier
    )
}

这个是ViewModel的注入,这里注解也是和老版本不同,需要注意

/**
 * 和 HiltModule相比,NetworkModule不是抽象类
 * 所以里头方法用的不是 @Bind注解,而是@Provides
 *
 *     @Singleton 保证全局只有一个实例
 *     @ActivityScoped 保证一个Activity只有一个实例
 */
@Module
@InstallIn(ActivityComponent::class)
class NetworkModule {

    @Singleton
    @Provides
    fun provideOkHttpClient(): OkHttpClient {
        return OkHttpClient.Builder()
            .addInterceptor(
                HttpLoggingInterceptor()
                    .apply {
                        setLevel(HttpLoggingInterceptor.Level.BODY)
                    },
            )
            .connectTimeout(20, TimeUnit.SECONDS)
            .readTimeout(20, TimeUnit.SECONDS)
            .writeTimeout(20, TimeUnit.SECONDS)
            .build()
    }

    @Singleton
    @Provides
    fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
        return Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create())
            .baseUrl("http://example.com/")
            .client(okHttpClient)
            .build()
    }
}

网络请求的注入方法,这里通过@Singleton保证全局单例,减少重复创建造成的浪费

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值