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保证全局单例,减少重复创建造成的浪费



1085

被折叠的 条评论
为什么被折叠?



