如何在 Kotlin 中创建和使用房间数据库 [Dagger-Hilt]

jso*_*onV 14 android kotlin android-room dagger-hilt

这是一个自我回答的问题,因为我的项目中有代表曝光问题,我打算早些时候提出这个问题,但经过几个小时的研究最终解决了这个问题。与其保持沉默,我认为这可能会在未来对某人有所帮助。本教程演示了如何创建 Room 数据库并在活动/片段中使用它。此处给出的示例用例是查询数据库的大小并更新片段中的视图。

注意:下面的代码中有一些 Dagger-Hilt 依赖注入,但如果您手动执行自己的依赖注入,则应采用相同的方法。我也希望你对 MVVM 架构有一些基本的了解。如果您对涉及 LiveData 的其他方法感兴趣,您可能会在此处找到有用的 Java 相关问题:资源 1资源 2;然而,重点是 Kotlin,这个解决方案不需要 LiveData。

jso*_*onV 34

您必须将项目中的 kotlin 文件关联起来,无论您的项目包的结构如何,但导入应该保持不变。在这种情况下,我使用 Dagger-Hilt 进行依赖注入以避免样板代码。

ItemsYouAreStoringInDB.kt

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "items")
data class ItemsYouAreStoringInDB(/*Parameter of Item entry*/) {
    @PrimaryKey(autoGenerate = true)
    var id: Int? = null
}
Run Code Online (Sandbox Code Playgroud)

你的道.kt

import androidx.room.*
@Dao
interface YourDAO {
    // Other insertion/deletion/query operations

    @Query("SELECT count(id) FROM items") // items is the table in the @Entity tag of ItemsYouAreStoringInDB.kt, id is a primary key which ensures each entry in DB is unique
    suspend fun numberOfItemsInDB() : Int // suspend keyword to run in coroutine
}
Run Code Online (Sandbox Code Playgroud)

你的数据库.kt

import androidx.room.Database
import androidx.room.RoomDatabase

@Database(
    entities = [ItemsYouAreStoringInDB::class], // Tell the database the entries will hold data of this type
    version = 1
)

abstract class YourDatabase : RoomDatabase() {

    abstract fun getYourDao(): YourDAO
}
Run Code Online (Sandbox Code Playgroud)

使用 Dagger-Hilt 进行依赖注入,YourRepository 能够被创建,因为 Dagger-Hilt 在幕后做一些事情,通过 YourDatabase 的抽象乐趣 getYourDao() YourRepository.kt提供一个 notificationDao

import path.to.ItemsYouAreStoringInDB
import path.to.YourDAO
import javax.inject.Inject // Dagger-Hilt to allow @Inject constructor

class YourRepository @Inject constructor(
    private val yourDAO: YourDAO
){
    // Other functions from YourDao.kt

    suspend fun numberOfItemsInDB() = yourDAO.numberOfItemsInDB()
}
Run Code Online (Sandbox Code Playgroud)

这不是关于如何使用 Dagger-Hilt 的演示,但需要以下两个文件:

应用模块.kt

import android.content.Context
import androidx.room.Room
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import dagger.hilt.android.qualifiers.ApplicationContext
import path.to.YourDatabase

import javax.inject.Singleton


@Module
@InstallIn(SingletonComponent::class)
object AppModule {

    @Singleton // Tell Dagger-Hilt to create a singleton accessible everywhere in ApplicationCompenent (i.e. everywhere in the application)
    @Provides
    fun provideYourDatabase(
        @ApplicationContext app: Context
    ) = Room.databaseBuilder(
        app,
        YourDatabase::class.java,
        "your_db_name"
    ).build() // The reason we can construct a database for the repo

    @Singleton
    @Provides
    fun provideYourDao(db: YourDatabase) = db.getYourDao() // The reason we can implement a Dao for the database
Run Code Online (Sandbox Code Playgroud)

基础应用程序.kt

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

@HiltAndroidApp 
class BaseApplication : Application() {}
Run Code Online (Sandbox Code Playgroud)

您还需要更新 AndroidManifest 文件并选择 BaseApplication 作为应用程序入口点,<application android:name="path.to.BaseApplication" ...以允许 Android 利用 Dagger-Hilt。

继续...

你的视图模型.kt

import dagger.hilt.android.lifecycle.HiltViewModel
import androidx.lifecycle.ViewModel
import path.to.YourRepository

@HiltViewModel
class MainViewModel @Inject constructor(
    private val repository: YourRepository
): ViewModel() {
    suspend fun databaseSize() : Int {
        return repository.numberOfItemsInDB()
    }
}
Run Code Online (Sandbox Code Playgroud)

现在您的 viewmodel 可以创建并且可以作为单例在整个应用程序中访问(它的两个实例不能存在),您可以在 Fragment/Activity 中使用它。视图模型可以访问存储库,该存储库可以通过查询 Room 数据库来接收信息。这是一个如何在片段中使用它的示例:

你的片段.kt

@AndroidEntryPoint // Dagger-Hilt requirement
class YourFragment : Fragment(R.layout.fragment_yourFragmentName) {
    private val viewModel: MainViewModel by viewModels()
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        setViewsBasedOnRepo() // You might want to call this in onResume()
    }

    private fun setViewsBasedOnRepo() { 
            GlobalScope.launch(Dispatchers.Main) { // Dispatchers.Main because only the Main thread can touch UI elements. Otherwise you may wish to use Dispatchers.IO instead!
                val size  =
                    withContext(Dispatchers.Default) { viewModel.databaseSize() }
                if (size == 0) { // Do stuff based on an empty database
                    btnAddItemsToDB.visibility = View.VISIBLE
                    textViewWarnNoItemsInDB.visibility = View.VISIBLE
                    recyclerViewItems.visibility = View.INVISIBLE
                } else { // Do other stuff when database has entries of type ItemsYouAreStoringInDB
                    btnAddItemsToDB.visibility = View.INVISIBLE                
                    textViewWarnNoItemsInDB.visibility = View.INVISIBLE
                    rvNotifications.visibility = View.VISIBLE
                }
            }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 简单又性感 (9认同)
  • @saket 在 Dagger-Hilt 中没有必要使用这些函数。当您构建应用程序时,由于“YourRepository.kt”中的“@Inject构造函数”,将自动创建依赖项注入样板代码。因此,在 `YourRepository.kt` 中,您可以使用 Dao 对象与数据库交互。同样,YourViewModel.kt 中的 @Inject 允许您访问存储库。然后从 Fragment/Activity 访问 viewModel。我刚刚意识到“YourRepository.kt”中的“MainRepository”命名错误。为了清楚起见,我将对其进行编辑。 (2认同)
  • 比官方文档更好!我认为“@ViewModelInject”可能已被“@HiltViewModel”+标准“@Inject”取代:https://developer.android.com/training/dependency-injection/hilt-jetpack#viewmodels (2认同)