我使用的是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操作只需要几毫秒。但是使用StateFlow
或Channel
将数据发送到Fragment/Activity的第一个选项是更一般和推荐的方式。你可以选择你喜欢的任何一个选项。