android 我应该如何使用房间数据库之间的协同程序,和流程?

3yhwsihp  于 2023-05-27  发布在  Android
关注(0)|答案(1)|浏览(133)

我正在尝试学习如何重写我的房间数据库(dao,repository,viewmodel),使其更有效。我没有使用过任何这些,我很难找到资源作为基础,因为他们中的许多人要么不使用repository(所以我开始认为我不必要地实现了它),要么他们使用hilt,我有点被新的东西淹没了。
我应该如何实现repository和viewmodel来拥有内部的流和协程?

ql3eal8s

ql3eal8s1#

好吧,你的问题很一般,但我会尽我所能回答,我想你至少有一个基本的协程,流和Hilt的知识,如果你没有,没有问题,至少尝试学习一些新的东西,我试图使它尽可能简单。

场景:

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

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

现在,让我们使用Flow和suspend函数创建一个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.
}

说明:
我们有三个功能:selectAll():检索收藏的书籍列表。insert():插入一本新书。delete():删除一本书。
要使插入和删除能够与协程一起工作,请为这两个函数添加suspend关键字。对于selectAll()函数,它返回一个流,你可以把它看作是LiveData的替代品,这可以让我们观察到当插入或删除一本新书时book表上的变化,selectAll()在插入/删除后会发出一个新的列表,允许你更新你的UI。注意selectAll()不是一个suspend函数,因为它返回一个流,流已经使用了协程,所以我们不需要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()作为挂起函数,因为您将insert()delete()声明为DAO中的挂起函数,并且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数据库、存储库和viewModel中将协程和流与应用程序集成的方式。您可以在流和协程上执行更高级的操作,以使您的应用程序更加健壮和高效。您可以根据应用程序的需求添加更多的操作和代码,我尝试用最简单的格式表示它。
最后,感谢您花时间阅读本文,希望对您有所帮助。

相关问题