我无法直接在 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 值的一些常规函数来“触发并忘记”。

此外,我认为您的代码有两个问题表明存在误解:

  1. 您的回购代码使用 withContext 包装了对 DAO 挂起函数的调用。没有理由在 withContext() 中包装一个挂起函数调用,因为 DAO 挂起函数已经在内部处理正确线程或调度程序上的调用。挂起函数永远阻塞以致需要特定 IO 或默认调度程序来调用它是不正确的。 withContext(Dispatchers.IO) 适用于调用 阻塞 IO 函数,例如直接使用 InputStreams 和 OutputStreams。

  2. 永远不要使用 async { }.await()。这毫无意义,与直接调用包装在 async 中的函数没有什么不同,因为无论如何您都在立即等待它们。