I am writing code to fetch data from Room
.
as shown in the image below, the IDE indicates that suspend
is a useless modifier.
So, if I remove suspend
, the following error occurs.
E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-1
Process: com.example.lightweight, PID: 6483
java.lang.NoSuchMethodError: No virtual method getWorkoutList(Lkotlin/coroutines/Continuation;)Ljava/lang/Object; in class Lcom/example/lightweight/data/repository/WorkoutListRepository; or its super classes (declaration of 'com.example.lightweight.data.repository.WorkoutListRepository' appears in /data/data/com.example.lightweight/code_cache/.overlay/base.apk/classes13.dex)
at com.example.lightweight.presentation.viewmodel.WorkoutListViewModel$getList$1.invokeSuspend(WorkoutListViewModel.kt:23)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42)
at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:749)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@6975010, Dispatchers.IO]
I/Process: Sending signal. PID: 6483 SIG: 9
However, if I leave suspend
as is, it runs normally. For what reason?
DAO
@Dao
interface WorkoutDao {
@Query("SELECT * From WorkoutListTest")
fun getWorkoutList() : List<WorkoutListTest>
}
ViewModel
class WorkoutListViewModel(
private val repository: WorkoutListRepository
) : ViewModel() {
private var _list = MutableLiveData<List<String>>()
val list: LiveData<List<String>>
get() = _list
fun getList() {
viewModelScope.launch(Dispatchers.IO) {
repository.getWorkoutList()
}
}
}
Repository
class WorkoutListRepository(private val dao: WorkoutDao) {
fun getWorkoutList() : List<WorkoutListTest> {
val list: List<WorkoutListTest> = dao.getWorkoutList()
return list
}
}
The part that uses ViewModel in Fragment.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
vm.getList()
vm.list.observe(viewLifecycleOwner) { list ->
}
}
CodePudding user response:
It's because your DAO function is not marked suspend. Since the DAO function is a regular synchronous, blocking function, calling through to it in your suspend function doesn't take advantage of the suspend
modifier. Incidentally, this would also mean your suspend function is blocking, which breaks convention and would cause your coroutines to block threads incorrectly. But the Kotlin compiler cannot detect that the DAO function is blocking, so the warning message doesn't mention that.
You need to mark your DAO function suspend
:
@Dao
interface WorkoutDao {
@Query("SELECT * From WorkoutListTest")
suspend fun getWorkoutList() : List<WorkoutListTest>
}
You said that leaving suspend
, it ran normally. This is because you launched your coroutine using Dispatchers.IO
. But you shouldn't have to use Dispatchers.IO
unless you're calling a blocking function. By convention, suspend functions never block. So if you were following conventions, you would not use Dispatchers.IO
to call a suspend function, but then your incorrectly blocking suspend function would hang the main thread.
Side note, there's a much easier way to fetch an item from a suspend function one time to fill in a LiveData. Your ViewModel class could be changed to the following and it would have the exact same behavior:
class WorkoutListViewModel(
private val repository: WorkoutListRepository
) : ViewModel() {
val list: LiveData<List<String>> = liveData {
emit(repository.getWorkoutList())
}
}