我无法直接在 Kotlin 中获取挂起函数的 return 值?
I have no way to get a return value of suspend function directly in Kotlin?
我在 Android Studio 项目中使用 Room。
希望能快速获取添加记录的ID,但是下面的代码A无法运行,请问如何解决?或者我必须像代码 B 一样使用这些代码?
我无法在 Kotlin 中直接获取挂起函数的 return 值?
代码A
@Dao
interface RecordDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun addRecord(aRecordEntity: RecordEntity): Long
}
class RecordRepository @Inject constructor(private val mRecordDao:RecordDao): IRecordRepository {
override suspend fun addRecord(aMRecord: MRecord): Long = withContext(Dispatchers.Default) {
mRecordDao.addRecord(ModelMapper.modelToEntity(aMRecord))
}
}
@HiltViewModel
class SoundViewModel @Inject constructor(
private val aRecordRepository: IRecordRepository
): ViewModel()
{
fun addRecord(aMRecord: MRecord):Long= viewModelScope.async {
aSoundMeter.addRecord(aMRecord)
}.await()
}
代码B
//The same
@HiltViewModel
class SoundViewModel @Inject constructor(
private val aRecordRepository: IRecordRepository
): ViewModel()
{
var id = 0L;
fun addRecord(aMRecord: MRecord) {
viewModelScope.launch {
id = aSoundMeter.addRecord(aMRecord)
}
}
}
您只能 return 来自另一个挂起函数的挂起函数的值。因此,将您的 ViewModel 函数设为 suspend
函数:
@HiltViewModel
class SoundViewModel @Inject constructor(
private val aRecordRepository: IRecordRepository
): ViewModel()
{
suspend fun addRecord(aMRecord: MRecord): Long =
aSoundMeter.addRecord(aMRecord)
}
并启动协程来调用它并在您的 Fragment 中处理结果:
viewLifecycleOwner.lifecycleScope.launch {
// ...
val id = viewModel.addRecord(record)
// do something with id inside same launched coroutine
}
请注意,如果您正在做一些对存储库状态至关重要的事情,您应该在 ViewModel 中启动的协程中使用 addRecord
的结果。在这种情况下,片段应该通过调用 ViewModel 中没有 return 值的一些常规函数来“触发并忘记”。
此外,我认为您的代码有两个问题表明存在误解:
您的回购代码使用 withContext
包装了对 DAO 挂起函数的调用。没有理由在 withContext()
中包装一个挂起函数调用,因为 DAO 挂起函数已经在内部处理正确线程或调度程序上的调用。挂起函数永远阻塞以致需要特定 IO 或默认调度程序来调用它是不正确的。 withContext(Dispatchers.IO)
适用于调用 阻塞 IO 函数,例如直接使用 InputStreams 和 OutputStreams。
永远不要使用 async { }.await()
。这毫无意义,与直接调用包装在 async
中的函数没有什么不同,因为无论如何您都在立即等待它们。
我在 Android Studio 项目中使用 Room。
希望能快速获取添加记录的ID,但是下面的代码A无法运行,请问如何解决?或者我必须像代码 B 一样使用这些代码?
我无法在 Kotlin 中直接获取挂起函数的 return 值?
代码A
@Dao
interface RecordDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun addRecord(aRecordEntity: RecordEntity): Long
}
class RecordRepository @Inject constructor(private val mRecordDao:RecordDao): IRecordRepository {
override suspend fun addRecord(aMRecord: MRecord): Long = withContext(Dispatchers.Default) {
mRecordDao.addRecord(ModelMapper.modelToEntity(aMRecord))
}
}
@HiltViewModel
class SoundViewModel @Inject constructor(
private val aRecordRepository: IRecordRepository
): ViewModel()
{
fun addRecord(aMRecord: MRecord):Long= viewModelScope.async {
aSoundMeter.addRecord(aMRecord)
}.await()
}
代码B
//The same
@HiltViewModel
class SoundViewModel @Inject constructor(
private val aRecordRepository: IRecordRepository
): ViewModel()
{
var id = 0L;
fun addRecord(aMRecord: MRecord) {
viewModelScope.launch {
id = aSoundMeter.addRecord(aMRecord)
}
}
}
您只能 return 来自另一个挂起函数的挂起函数的值。因此,将您的 ViewModel 函数设为 suspend
函数:
@HiltViewModel
class SoundViewModel @Inject constructor(
private val aRecordRepository: IRecordRepository
): ViewModel()
{
suspend fun addRecord(aMRecord: MRecord): Long =
aSoundMeter.addRecord(aMRecord)
}
并启动协程来调用它并在您的 Fragment 中处理结果:
viewLifecycleOwner.lifecycleScope.launch {
// ...
val id = viewModel.addRecord(record)
// do something with id inside same launched coroutine
}
请注意,如果您正在做一些对存储库状态至关重要的事情,您应该在 ViewModel 中启动的协程中使用 addRecord
的结果。在这种情况下,片段应该通过调用 ViewModel 中没有 return 值的一些常规函数来“触发并忘记”。
此外,我认为您的代码有两个问题表明存在误解:
您的回购代码使用
withContext
包装了对 DAO 挂起函数的调用。没有理由在withContext()
中包装一个挂起函数调用,因为 DAO 挂起函数已经在内部处理正确线程或调度程序上的调用。挂起函数永远阻塞以致需要特定 IO 或默认调度程序来调用它是不正确的。withContext(Dispatchers.IO)
适用于调用 阻塞 IO 函数,例如直接使用 InputStreams 和 OutputStreams。永远不要使用
async { }.await()
。这毫无意义,与直接调用包装在async
中的函数没有什么不同,因为无论如何您都在立即等待它们。