是否有必要将DAO内部的函数定义为挂起?

Ioa*_* P. 3 android kotlin android-room kotlin-coroutines

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

interface ItemDao {
    @Insert(onConflict = IGNORE)
    fun addItem(item: Item)
}
Run Code Online (Sandbox Code Playgroud)

以及存储库接口:

interface ItemRepository {
    fun addItem(item: Item)
}

Run Code Online (Sandbox Code Playgroud)

下面是该函数的实现:

class ItemRepositoryImpl(
    private val itemDao: ItemDao
) : ItemRepository {
    override fun addItem(item: Item) = itemDao.addItem(item)
}
Run Code Online (Sandbox Code Playgroud)

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

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

当我addItem从 UI 调用时,该项目已成功添加到 Room。现在的问题是,是否必须将ItemDao接口内的addItem函数设置为挂起函数?如果是,为什么?我真的很困惑。

编辑

这是我创建数据库实例的方法:

fun provideItemDb(
    @ApplicationContext
    context : Context
) = Room.databaseBuilder(
    context,
    ItemDb::class.java,
    "item_table"
).build()
Run Code Online (Sandbox Code Playgroud)

Ten*_*r04 5

挂起函数不是必需的,但如果您不将函数标记为suspend,那么它们会阻塞,因此在主线程上调用它们是不安全的。

如果您正在使用协程,则调用阻塞 IO 函数是安全的,但前提是您当前的 CoroutineContext 未使用Dispatchers.Main. 您目前没有使用suspend函数,因为您正在使用 运行协程Dispatchers.IO,因此该函数是从主线程调用的。

如果您正在使用协程,为了代码简单起见,您应该始终对它们进行标记suspend,以便这些函数更容易使用且更通用。例如,想象一下,如果您还想使用新添加项目的行 ID 更新 LiveData。(您可以通过在 DAO 函数中返回 Long 来完成此操作。)

suspend如果您在 DAO 中使用,您的函数可以是:

fun addItem(item: Item) = viewModelScope.launch {
    _latestRowLiveData.value = repo.addItem(item)
}
Run Code Online (Sandbox Code Playgroud)

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

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
    }
}
Run Code Online (Sandbox Code Playgroud)

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