如何在viewmodelScope中从协程返回值?



我使用的是Room,我需要将id返回到Fragment,当insert()返回时。

但是,但是我不能从viewModelScope返回值。

我看到了其他类似的问题,但答案是返回LiveData

我不需要LiveData。我只想返回Long类型的值

我该怎么做?


回购

class WorkoutListRepository(private val dao: WorkoutDao) {
@RequiresApi(Build.VERSION_CODES.O)
suspend fun createDailyLog(part: BodyPart) : Long {
...
return dao.insertDailyLog(data)
}
}

ViewModel

class WorkoutListViewModel(
private val repository: WorkoutListRepository
) : ViewModel() {

...

@RequiresApi(Build.VERSION_CODES.O)
fun createDailyLog(part: BodyPart) : Long {
viewModelScope.launch(Dispatchers.IO) {
return@launch repository.createDailyLog(part) // can't return
}
}
}

class WorkoutListTabPagerFragment : Fragment(), WorkoutListAdapter.OnItemClickListener {
...

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentWorkoutListTabPagerBinding.inflate(inflater, container, false)
...
return binding.root
}

@RequiresApi(Build.VERSION_CODES.O)
override fun onItemClick(workout: String) {
when(PageState.curPageState) {
is PageState.startWorkout -> {
val id = vm.createDailyLog(part)
...
}
is PageState.addWorkout -> //TODO:
is PageState.editWorkout -> //TODO:
}
}
}

但是我不需要LiveData

。因为launch内部的代码是异步的,所以你需要某种可观察数据持有人。它不会立即运行。它只是被安排执行而已。另一方面,launch函数立即返回,即ViewModel中的createDailyLog函数在调用repository.createDailyLog(part)之前返回。所以你不能从异步方法中同步返回一个值。

您可以使用LiveData或Kotlin的StateFlow将此数据发送到Fragment。您的片段将观察到该状态的变化并做出相应的响应。我建议在这里使用StateFlow。代码看起来像这样:

// ViewModel
class WorkoutListViewModel(
private val repository: WorkoutListRepository
) : ViewModel() {

private val _logIdFlow = MutableStateFlow<Long?>(null)
val logIdFlow = _logIdFlow.asStateFlow()
...

@RequiresApi(Build.VERSION_CODES.O)
fun createDailyLog(part: BodyPart) : Long {
viewModelScope.launch(Dispatchers.IO) {
_logIdFlow.value = repository.createDailyLog(part)
}
}
}
// Fragment
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentWorkoutListTabPagerBinding.inflate(inflater, container, false)
...
viewLifecycleOwner.lifecycleScope.launch {
viewModel.logIdFlow.collect { logId ->
if(logId != null) {
// Do whatever you want with the log Id
}
}
}
return binding.root
}

另一种解决方案是使用KotlinChannel并通过该Channel发送数据。

如果你只是需要一个快速、简短的解决方案,你可以直接从Fragment的生命周期范围调用repository函数,像这样:

// ViewModel
suspend fun createDailyLog(part: BodyPart) : Long {
return repository.createDailyLog(part)
}
//Fragment
override fun onItemClick(workout: String) {
viewLifecycleOwner.lifecycleScope.launch {
when(PageState.curPageState) {
is PageState.startWorkout -> {
val id = vm.createDailyLog(part) // This will now work
...
}
is PageState.addWorkout -> //TODO:
is PageState.editWorkout -> //TODO:
}
}
}

这个解决方案的唯一问题是,现在db操作绑定到片段的生命周期。因此,如果有任何事件破坏的片段的生命周期(如配置更改),操作将被取消。这应该不是一个大问题,因为您的db操作只需要几毫秒。但是使用StateFlowChannel将数据发送到Fragment/Activity的第一个选项是更一般和推荐的方式。你可以选择你喜欢的任何一个选项。

相关内容

  • 没有找到相关文章

最新更新