Android SQLDelight (四) Hilt 将 database 依赖注入 ViewModel

更新日期: 2023-05-26 阅读次数: 1079 字数: 858 分类: Android

由于使用的 Navigation UI 会在 navigate 时销毁 fragment 的 View,实际操作时,由 list to detail to list 这样的浏览路径,会导致 fragment onViewCreated 中的逻辑执行多次。这是不合理的,所以需要将相关逻辑挪到 view model 中 (fragment view 销毁了,但是 fragment 没有销毁,所以 view model 是安全的)。而 view model 中不应该去获取 fragment 或 activity 的实例,也就没法拿到之前偷懒放到 activity 中的 SQLDeligth Database 实例,所以只能使用依赖注入方式了。

Hilt 与 Koin 的艰难抉择

大家都说 Koin 上手简单,使用门槛比 Hilt 低很多。 但是,Koin 的官方文档太简略了,完全不知道怎么结合 SQLDelight 使用。 而且资料太少了,查都查不到类似的使用场景。

Hilt 虽然啰嗦了一点,但是好在文档够全,Google Android 开发者的微信公众号上的文章也发了多篇进行介绍, 每篇质量都很高,原理说的也清晰,用起来反而心理有底。虽然之前也整理过 Android Hilt 实现依赖注入的自动化管理 , 但其实并没有实际动手操作过,理解不深。这两天,睡前/坐车时翻了几篇公众号文章,并梳理了代码,感觉大体配置方法已了然于胸。就等今晚大试身手了。

安装依赖

项目的根级 build.gradle 文件

plugins {
  id 'com.google.dagger.hilt.android' version '2.44' apply false
}

app/build.gradle 文件

plugins {
  id 'kotlin-kapt'
  id 'com.google.dagger.hilt.android'
}

dependencies {
  implementation "com.google.dagger:hilt-android:2.44"
  kapt "com.google.dagger:hilt-compiler:2.44"
}

// Allow references to generated code
kapt {
  correctErrorTypes true
}

application

所有使用 Hilt 的应用都必须包含被 @HiltAndroidApp 注解的 Application 类,它会在编译期触发 Hilt 的代码生成。

新建一个 MainApplication.kt 类文件:

import android.app.Application
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class MainApplication: Application()

同时在 manifest 文件中补充 application name:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:name=".MainApplication"
...

否则会在运行时报错:

Hilt Activity must be attached to an @HiltAndroidApp Application. Did you forget to specify your Application's class name in your manifest's 's android:name attribute?

Hilt 的错误提示很到位。

module

在添加了 @Provides 注解的方法上,我们可以通过使用 @Singleton 注解来告诉 Hilt 组件总是共享该类型的相同实例。

新建一个 DataModule.kt 文件:

import android.content.Context
import app.cash.sqldelight.db.SqlDriver
import app.cash.sqldelight.driver.android.AndroidSqliteDriver
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object DataModule {
    @Singleton
    @Provides
    fun provideDatabase(@ApplicationContext context: Context): Database {
        val driver: SqlDriver = AndroidSqliteDriver(Database.Schema, context, "item_database.db")
        return Database(driver)
    }
}

kotlin object 代表整个 app 只有唯一一个实例

view model

为了让 Hilt 知道如何提供该 ViewModel 的实例,我们不仅要在构造函数上添加 @Inject 注解,还需要对这个类添加 @HiltViewModel 注解。

@HiltViewModel
class HomeViewModel @Inject constructor(
    database: Database
): ViewModel() {

    val items = database.itemQueries.selectAll()
        .asFlow()
        .mapToList(Dispatchers.IO)

}

fragment

使用 view model 的 fragment 需要加上注解 @AndroidEntryPoint.

@AndroidEntryPoint
class HomeFragment : Fragment() {
    private val homeViewModel: HomeViewModel by viewModels()

	// ...
}

activity

fragment 所依附的 activity 也需要加上 @AndroidEntryPoint 注解。

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    // ...
}

否则报错:

java.lang.IllegalStateException: Hilt Fragments must be attached to an @AndroidEntryPoint Activity.

Java 版本号问题

可能是因为用了最新版 Android Studio 的原因,编译时报错:

'compileDebugJavaWithJavac' task (current target is 1.8) and 'kaptGenerateStubsDebugKotlin' task (current target is 17) jvm target compatibility should be set to the same Java version.

修正 app build.gradle 中的 java 版本由 1.8 升级为 17.

compileOptions {
	sourceCompatibility JavaVersion.VERSION_17
	targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
	jvmTarget = '17'
}

探索更多关于 SQLDelight

📖 Android Room 替代品 SQLDelight 中文入门教程

参考

  • https://developer.android.com/training/dependency-injection/hilt-android?hl=zh-cn#groovy
  • https://mp.weixin.qq.com/s/iixQXwajchSlLdVhSMsFqA
  • https://mp.weixin.qq.com/s/qfrjA7KxlG69t1cpYEQB6g

tags: sqldelight hilt

关于作者 🌱

我是来自山东烟台的一名开发者,有敢兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式