在协程和流程中我应该如何使用房间数据库?

问题描述 投票:0回答:1

我正在尝试学习如何重写我的房间数据库(dao、存储库、viewmodel)以提高效率。我没有使用过这些中的任何一个,而且我很难找到可以作为基础的资源,因为他们中的许多人要么不使用存储库(所以我开始认为我已经不必要地实现了它)或者他们正在使用刀柄,我对要学习的新东西有点不知所措。

我应该如何实现存储库和视图模型以在其中包含流和协程?

android mvvm android-room kotlin-coroutines flow
1个回答
0
投票

好吧,你的问题很笼统,但我会尽力回答,我想你至少对协程、流和 Hilt 有基本的了解,如果你没有,没关系,至少尝试学习一些东西新的,我尽量让它简单。

场景:

假设有一个简单的应用程序向用户显示书籍信息,用户可以将任何书籍添加到收藏夹,从收藏夹中删除它们,并有一个屏幕来显示收藏的书籍。

我们有一个名为 Book 的简单实体类:

@Entity
data class Book(
   @PrimaryKey
   val ispn: String
   val title: String,
   val description: String,
   val pages: Int
)

现在,让我们创建一个带有 Flow 和挂起函数的 DAO 接口:

@Dao
interface FavoriteBooksDao {
    @Query("SELECT * FROM book")
    fun selectAll(): Flow<List<Book>> // No need to add suspend keyword, because it returns Flow, flows already uses coroutines.

    @Insert
    suspend fun insert(book: Book) // Simply, add suspend keyword to the function, to make it work with coroutines.

    @Delete
    suspend fun delete(book: Book) // Simply, add suspend keyword to the function, to make it work with coroutines.
}

说明:

我们有3个功能:

selectAll()
:检索最喜欢的书的列表。
insert()
:插入一本新书。
delete()
:删除一本书。

要使插入和删除与协程一起工作,请为这两个函数添加

suspend
关键字。对于
selectAll()
函数,它返回一个流,你可以把它看作是对
LiveData
的替代,这样我们就可以在插入或删除新书时观察
book
表上的变化,
selectAll()
将在插入/删除后发出一个新列表,允许您更新您的 UI。请注意,
selectAll()
不是挂起函数,因为它返回一个流,流已经使用协程,所以我们不需要
suspend
关键字。

现在让我们创建存储库:

class FavoriteBooksRepository @Inject constructor(
    private val dao: FavoriteBooksDao
) {

    fun getAll() = dao.selectAll() //Non-suspending function

    suspend fun add(book: Book) = dao.insert(book) //suspending function


    suspend fun remove(book: Book) = dao.delete(book) //suspending function

}

说明:

现在,您需要存储库中的 DAO 实例,使用 Hilt 注入它。

您在存储库中有 3 个函数将调用 DAO 函数,您将有

add()
remove()
作为挂起函数,因为您在 DAO 中声明
insert()
delete()
作为挂起函数,而
getAll()
是不要在你的 DAO 中像
selectAll()
那样暂停,因为它会返回一个流程,如前所述。

最后,让我们实现ViewModel:

@HiltViewModel
class FavoriteBooksViewModel @Inject constructor(
    private val repository: FavoriteBooksRepository
): ViewModel() {

    // This is a mutable state flow that will be used internally in the viewmodel, empty list is given as initial value.
    private val _favoriteBooks = MutableStateFlow(emptyList<Book>())

    //Immutable state flow that you expose to your UI
    val favoriteBooks = _favoriteBooks.asStateFlow()

     init {
          getFavoriteBooks()
     }


    /**
     * This function is used to get all the books from the database, and update the value of favoriteBooks.
     * 1. viewModelScope.launch is used to launch a coroutine within the viewModel lifecycle.
     * 2. repository.getAll() is used to get all the books from the database.
     * 3. flowOn(Dispatchers.IO) is used to change the dispatcher of the flow to IO, which is optimal for IO operations, and does not block the main thread.
     * 4. collect is a suspending function used to collect the flow of books list, and assign the value to favoriteBooks.
     * 5. each time the flow emits a new value, the collect function will be called with the list of books.
     */
    fun getFavoriteBooks() {
        viewModelScope.launch { //this: CoroutineScope
            repository.getAll().flowOn(Dispatchers.IO).collect { books: List<Book> ->
                _favoriteBooks.update { books }
            }
        }
    }

    /**
     * This function is used to add a book to the database.
     * 1. viewModelScope.launch is used to launch a coroutine within the viewModel lifecycle.
     * 2. Dispatchers.IO is used to change the dispatcher of the coroutine to IO, which is optimal for IO operations, and does not block the main thread.
     * 3. repository.add(book) is used to add the book to the database.
     */
    fun addBook(book: Book) {
        viewModelScope.launch(Dispatchers.IO) { //this: CoroutineScope
            repository.add(book)
        }
    }

    /**
     * This function is used to remove a book from the database.
     * 1. viewModelScope.launch is used to launch a coroutine within the viewModel lifecycle.
     * 2. Dispatchers.IO is used to change the dispatcher of the coroutine to IO, which is optimal for IO operations, and does not block the main thread.
     * 3. repository.remove(book) is used to remove the book from the database.
     */
    fun removeBook(book: Book) {
        viewModelScope.launch(Dispatchers.IO) { //this: CoroutineScope
            repository.remove(book)
        }
    }
}

说明:

现在,您需要 viewModel 中的 Repository 实例,使用 Hilt 注入它。

我添加了文档,分别描述了所有的工作和功能,尽可能清楚,还要注意功能不是挂起的,因为我们启动了一个

viewModelScope
协程,就足够了,不需要标记这些功能作为暂停。

这就是您在 Room 数据库、存储库和视图模型中将协程和流与您的应用程序集成的方式。随着您了解更多,您可以对流和协程执行更高级的操作,以使您的应用程序更加健壮和高效。你可以根据你的应用需求添加更多的操作和代码,我尽量用最简单的格式来表示。

最后,感谢您花时间阅读本文,希望对您有所帮助。

© www.soinside.com 2019 - 2024. All rights reserved.