Home > Software engineering >  I have no way to get a return value of suspend function directly in Kotlin?
I have no way to get a return value of suspend function directly in Kotlin?

Time:03-26

I use Room in my Android Studio project.

I hope to get the ID of added record quickly, but the following Code A can't work, how can I fix it? or I have to use these code just like Code B?

I have no way to get a return value of suspend function directly in Kotlin ?

Code 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()   
}

Code 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)
        }
    }
}

CodePudding user response:

You can only return the value of a suspend function from another suspend function. So make your ViewModel's function a suspend function:

@HiltViewModel
class SoundViewModel @Inject constructor(
    private val aRecordRepository: IRecordRepository
): ViewModel()
{
    suspend fun addRecord(aMRecord: MRecord): Long = 
        aSoundMeter.addRecord(aMRecord)
 
}

And launch a coroutine to call it and work with the results in your Fragment:

viewLifecycleOwner.lifecycleScope.launch {
    // ...
    val id = viewModel.addRecord(record)
    // do something with id inside same launched coroutine
}

Note, if you're doing something critical to repository state, you should be using the results of addRecord inside a coroutine launched in the ViewModel instead. In that case, the Fragment should just "fire and forget" by calling some regular function in the ViewModel that has no return value.

Also, there are two issues with your code that I think show a misunderstanding:

  1. Your repo code wraps the call to the DAO's suspend function using withContext. There is no reason to wrap a suspend function call in withContext() because the DAO suspend function already internally handles calling things on the right threads or dispatchers. It would be incorrect for a suspend function to ever block such that a specific IO or Default Dispatcher would be needed to call it. withContext(Dispatchers.IO) is for when you are calling blocking IO functions, like directly working with InputStreams and OutputStreams.

  2. Never use async { }.await(). That's pointless and no different than directly calling the functions that you are wrapping in async since you are waiting for them immediately anyway.

  • Related