Как создать и использовать базу данных комнат в Kotlin [Dagger-Hilt]

Это вопрос, на который я ответил сам, и я собирался задать его ранее, поскольку у меня были проблемы с репутацией в моем проекте, но в итоге я решил их через пару часов исследования. Я подумал, что вместо того, чтобы хранить молчание, это может кому-то помочь в будущем. В этом руководстве показано, как создать базу данных Room и использовать ее в действии / фрагменте. Приведенный здесь пример использования - это запрос размера вашей базы данных и обновление представлений во фрагменте.

Примечание. В приведенном ниже коде есть некоторая инъекция зависимостей Dagger-Hilt, но та же методология должна применяться, если вы вручную выполняете собственную инъекцию зависимостей. Я также ожидаю, что у вас есть базовые знания об архитектуре MVVM. Здесь вы можете найти полезные вопросы, связанные с Java, если вас интересуют другие способы сделать это с использованием LiveData: ресурс 1, ресурс 2; однако основное внимание уделяется Kotlin, и это решение не требует LiveData.


person jsonV    schedule 29.07.2020    source источник


Ответы (1)


Вам нужно будет связать файлы 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
}

YourDao.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
}

YourDatabase.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
}

Используя Dagger-Hilt для внедрения зависимостей, YourRepository можно создать, поскольку Dagger-Hilt делает что-то под капотом, чтобы предоставить notificationDao через абстрактное развлечение YourDatabase getYourDao () YourRepository.kt

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()
}

Это не демонстрация того, как использовать Dagger-Hilt, но необходимы следующие два файла:

AppModule.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

BaseApplication.kt

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

@HiltAndroidApp 
class BaseApplication : Application() {}

Вам также потребуется обновить файл AndroidManifest и выбрать BaseApplication в качестве точки входа в приложение <application android:name="path.to.BaseApplication" ..., чтобы Android мог использовать Dagger-Hilt.

Продолжаем ...

YourViewModel.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()
    }
}

Теперь, когда ваша модель представления может быть создана и доступна для всего приложения как синглтон (два ее экземпляра не могут существовать), вы можете использовать ее во фрагменте / действии. Модель просмотра имеет доступ к репозиторию, который может получать информацию, запрашивая базу данных Room. Вот пример того, как вы можете использовать это во фрагменте:

YourFragment.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
                }
            }
    }
}
person jsonV    schedule 29.07.2020
comment
просто и сексуально - person Sai; 23.11.2020
comment
где мы используем функции provideYourDao и provideYourDatabase модуля приложения? - person saket; 19.01.2021
comment
@saket В Dagger-Hilt нет необходимости использовать эти функции. Когда вы создаете приложение, шаблонный код внедрения зависимостей будет создан автоматически из-за @Inject constructor в YourRepository.kt. Таким образом, внутри YourRepository.kt вы можете использовать объекты Dao для взаимодействия с базой данных. Точно так же @Inject в YourViewModel.kt позволяет вам получить доступ к репозиторию. И затем доступ к viewModel осуществляется из фрагмента / действия. Я только что понял, что MainRepository в YourRepository.kt неправильно назван. Я отредактирую это для ясности. - person jsonV; 20.01.2021
comment
Лучше официальной документации! Я думаю, что @ViewModelInject можно было бы заменить на @HiltViewModel + стандартный @Inject: разработчик. android.com/training/dependency-injection/ - person charles-allen; 25.02.2021
comment
Спасибо, @ charles-allen, ты слишком добрый! Я внес изменения, а также использовал более удачное название @SingletonComponent вместо @ApplicationComponent. Будущим читателям следует понимать, что могут быть и другие случаи отказа от поддержки, поскольку это не официальный документ. Не стесняйтесь вносить правки или комментировать. - person jsonV; 05.03.2021