kotlin 有必要将DAO中的函数定义为suspend吗?

js5cn81o  于 2023-03-19  发布在  Kotlin
关注(0)|答案(2)|浏览(207)

我正在使用Room创建一个简单的应用程序,我创建了一个DAO接口:

interface ItemDao {
    @Insert(onConflict = IGNORE)
    fun addItem(item: Item)
}

和存储库接口:

interface ItemRepository {
    fun addItem(item: Item)
}

下面是函数的实现:

class ItemRepositoryImpl(
    private val itemDao: ItemDao
) : ItemRepository {
    override fun addItem(item: Item) = itemDao.addItem(item)
}

在ViewModel类中,我使用viewModelScope启动了一个协同程序:

class ItemViewModel @Inject constructor(
    private val repo: ItemRepository
) : ViewModel() {
    fun addItem(item: Item) = viewModelScope.launch(Dispatchers.IO) {
        repo.addItem(item)
    }
}

我在UI调用addItem的时候,已经成功的将物品添加到Room了,现在的问题是,ItemDao接口里面的addItem函数是不是一定要设置为suspend函数,如果是的话,为什么呢?我真的很困惑。

编辑

下面是我创建数据库示例的方法:

fun provideItemDb(
    @ApplicationContext
    context : Context
) = Room.databaseBuilder(
    context,
    ItemDb::class.java,
    "item_table"
).build()
tsm1rwdh

tsm1rwdh1#

Suspend函数不是必需的,但是如果您不将这些函数标记为suspend,则它们会阻塞,因此在主线程上调用它们是不安全的。
如果你正在使用协程,调用一个阻塞IO函数是安全的,但只有当你当前的CoroutineContext没有使用Dispatchers.Main时。你当前没有使用suspend函数,因为你正在使用Dispatchers.IO运行你的协程,所以函数是从主线程调用的。
如果你使用协程,为了代码的简单,你应该总是把它们标记为suspend,这样函数就更容易和更通用了。想象一下,例如,你还想用新添加项的行ID更新LiveData。(你可以通过在DAO函数中返回一个Long来实现这一点。)
如果在DAO中使用suspend,则函数可能是:

fun addItem(item: Item) = viewModelScope.launch {
    _latestRowLiveData.value = repo.addItem(item)
}

如果在DAO中没有使用suspend,那么函数必须是以下更复杂的实现之一:

fun addItem(item: Item) = viewModelScope.launch {
    _latestRowLiveData.value = withContext(Dispatchers.IO) { repo.addItem(item) }
}

// or

fun addItem(item: Item) = viewModelScope.launch(Dispatchers.IO) {
    _latestRowLiveData.postValue(repo.addItem(item))
}

// or

fun addItem(item: Item) = viewModelScope.launch(Dispatchers.IO) {
    val rowId = repo.addItem(item)
    withContext(Dispatchers.Main) {
        _latestRowLiveData.value = rowId
    }
}

现在想象一个函数想要执行一系列不同的操作,DAO的非挂起版本会导致ViewModel中的协程看起来非常混乱(因此容易出错)。

jvlzgdj9

jvlzgdj92#

现在的问题是,是否必须将ItemDao接口中的addItem函数设置为suspend函数?

  • 强制性 *?不(你自己看,因为它的工作)。
  • 推荐 *?是的。

如果是,为什么?我真的很困惑。
您是否希望每次调用IO时都必须记住在协程中使用IO上下文调用addItem?或者您是否希望无论您从何处调用它,它都能正常工作(在IO线程上的协程中运行)?
这就是为什么要将其设置为suspend * 并在调用中切换到IO上下文。

相关问题