函数无法返回带有协程块的项目

I am retrieving a record from a Room database using Coroutines because it has to run in a background thread. I want to return the result through the function.

class LessonRepository(val app: Application) {

    private val courseDao = MyDatabase.getDatabase(app).courseDao()
}

    fun getCourseData(): Course {

        var course: Course

        CoroutineScope(Dispatchers.IO).launch {
            course = courseDao.getCourse(globalSelectedCourse)
        }
        return course
    }

视图模型

class LessonViewModel(app: Application): AndroidViewModel(app) {

    private val lessonDataRepository = LessonRepository(app)
    val lessonData = lessonDataRepository.lessonData
    val selectedLesson = MutableLiveData<Lesson>()

    fun getCourseData() : Course {
        return lessonDataRepository.getCourseData()
    }
}

我想在片段中使用返回值:

class DetailFragment : Fragment(), LessonRecyclerAdapter.LessonItemListener {
.
.
.
        viewModel = ViewModelProvider(this).get(LessonViewModel::class.java)

        val course = viewModel.getCourseData()
.
.
.
    }

However, Android Studio is giving me an error indicator in the return statement return course that course must be initialized. How can i successfuly return the value of course?

-更新:-

我正在尝试获取该记录的值,并将其用于片段中,如下所示:

val course = viewModel.viewModelScope.launch { viewModel.getCourseData() }

textViewName.text = course.Name
textViewInstructor.text = course.instructor
评论
木叶
木叶

您这样做的方式是错误的。也许您对并发或并发运行的任务有一些误解。

让我清除您的疑虑。

  1. launch does not block, it is launch and forget. You never know when the value is set. Only thing you can do is call join() on that to make sure it is done.
  2. But still using a launch block is not very well optimized for tasks where you want a result back from a coroutine. We use async/withContext here.
  3. async is called on a CoroutineScope while a withContext is a top level function which expects a CoroutineContext as its parameter.
  4. withContext suspends the caller coroutine till it is completed. While the async does not, the return value of async is Deferred<T> on which when you call .await() the caller coroutine gets suspended till it the task is completed similar to withContext.

因此,您可以通过以下方式完成任务。

选项1:最优化的版本

使函数挂起并与withContext一起使用。它将暂停调用协程直到获取课程。

suspend fun getCourseData(): Course {
    return withContext(Dispatchers.IO) {
        courseDao.getCourse(globalSelectedCourse)
    }
}

// or simpler
suspend fun getCourseData(): Course =
    withContext(Dispatchers.IO) {
        courseDao.getCourse(globalSelectedCourse)
    }

选项2:使用异步,然后返回Deferred。

// declare scope elsewhere. It is not intended to create scope everytime you want to launch a task
val scope = CoroutineScope(Dispatchers.IO)

// using async at the end of function is a naming scheme by Kotlin recommendation.
fun getCourseDataAsync(): Deferred<Course> =
    scope.async {
        courseDao.getCourse(globalSelectedCourse)
    }

//Now when you call the function, call await(), it is suspending, it will suspend the calling coroutine till the course is fetched.
val course: Course = getCourseDataAsync().await()

更新OP的更新

正如我在评论中所建议的那样,您不能在协程块之外使用暂停的代码块。因为您不能暂停非暂停功能。

进行如下操作:

// in fragment
suspend fun getCourseData() : Course {
    return lessonDataRepository.getCourseData()
}

viewModel.viewModelScope.launch {
    val course = viewModel.getCourseData()

    textViewName.text = course.Name
    textViewInstructor.text = course.instructor
}
点赞
评论