Android 函数无法返回带有协同程序块的项

Android 函数无法返回带有协同程序块的项,android,kotlin,android-room,kotlin-coroutines,Android,Kotlin,Android Room,Kotlin Coroutines,我使用协程从房间数据库检索记录,因为它必须在后台线程中运行。我想通过函数返回结果 class LessonRepository(val app: Application) { private val courseDao = MyDatabase.getDatabase(app).courseDao() } fun getCourseData(): Course { var course: Course CoroutineScope(Disp

我使用协程从房间数据库检索记录,因为它必须在后台线程中运行。我想通过函数返回结果

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()
    }
}
然而,androidstudio在return语句return course中给了我一个错误指示符,表示必须初始化该课程。当然,我如何才能成功地返回值

-更新:-

我试图获取该记录的值,并在片段中使用它,如下所示:

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

textViewName.text = course.Name
textViewInstructor.text = course.instructor

你这样做是错误的。可能您对并发性或并发运行的任务有一些误解

让我澄清你的疑虑

发射不阻塞,它是发射和遗忘。您永远不知道何时设置该值。你唯一能做的就是打电话给join,确保它完成了。 但是仍然使用启动块并不能很好地优化您希望从协同路由返回结果的任务。我们在这里使用async/withContext。 async在CoroutineScope上调用,而withContext是一个顶级函数,它需要CoroutineContext作为其参数。 withContext暂停调用方协同路由,直到完成。虽然async没有,但async的返回值在调用时会被延迟。等待调用方协同程序挂起,直到任务完成,类似于withContext。 因此,您可以按以下方式完成任务

选项1:最优化的版本

使函数挂起并与上下文一起使用。它将暂停调用协同程序,直到获取课程

暂停趣味课程数据:课程{ 使用contextdispatchers.IO返回{ courseDao.getCourseglobalSelectedCourse } } //或者更简单 暂停趣味课程数据:课程= withContextDispatchers.IO{ courseDao.getCourseglobalSelectedCourse } 选项2:使用async,并返回延迟的

//在别处声明范围。它并不打算在每次启动任务时创建作用域 val scope=CoroutineScopeDispatchers.IO //在函数末尾使用async是Kotlin推荐的命名方案。 fun getCourseDataAsync:延迟= scope.async{ courseDao.getCourseglobalSelectedCourse } //现在,当您调用函数callwait时,它是挂起的,它将挂起调用的协程,直到获取课程为止。 val课程:课程=getCourseDataAsync.await OP最新消息 正如我在评论中所建议的那样,您不能将挂起的代码块置于协同程序块之外。因为您不能挂起非挂起函数

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
    }
按以下步骤进行:

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

textViewName.text = course.Name
textViewInstructor.text = course.instructor
//零碎 暂停趣味课程数据:课程{ 返回lessonDataRepository.getCourseData } viewModel.viewModelScope.launch{ val course=viewModel.getCourseData textViewName.text=课程名称 text查看讲师。text=课程。讲师 }
你这样做是错误的。可能您对并发性或并发运行的任务有一些误解

让我澄清你的疑虑

发射不阻塞,它是发射和遗忘。您永远不知道何时设置该值。你唯一能做的就是打电话给join,确保它完成了。 但是仍然使用启动块并不能很好地优化您希望从协同路由返回结果的任务。我们在这里使用async/withContext。 async在CoroutineScope上调用,而withContext是一个顶级函数,它需要CoroutineContext作为其参数。 withContext暂停调用方协同路由,直到完成。虽然async没有,但async的返回值在调用时会被延迟。等待调用方协同程序挂起,直到任务完成,类似于withContext。 因此,您可以按以下方式完成任务

选项1:最优化的版本

使函数挂起并与上下文一起使用。它将暂停调用协同程序,直到获取课程

暂停趣味课程数据:课程{ 使用contextdispatchers.IO返回{ courseDao.getCourseglobalSelectedCourse } } //或者更简单 暂停趣味课程数据:课程= withContextDispatchers.IO{ courseDao.getCourseglobalSelectedCourse } 选项2:使用async,并返回延迟的

//在别处声明范围。它并不打算在每次启动任务时创建作用域 val scope=CoroutineScopeDispatchers.IO //在函数末尾使用async是Kotlin推荐的命名方案。 fun getCourseDataAsync:延迟= scope.async{ courseDao.getCourseglobalSelectedCourse } //现在,当您调用函数callwait时,它是挂起的,它将挂起调用的协程,直到获取课程为止。 val课程:课程=getCourseDataAsync.await OP最新消息 正如我在评论中所建议的那样,您不能将挂起的代码块置于协同程序块之外。因为您不能挂起非挂起函数

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
    }
按以下步骤进行:

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

textViewName.text = course.Name
textViewInstructor.text = course.instructor
//零碎 暂停 结束有趣的课程数据:课程{ 返回lessonDataRepository.getCourseData } viewModel.viewModelScope.launch{ val course=viewModel.getCourseData textViewName.text=课程名称 text查看讲师。text=课程。讲师 }
您应该利用Room自2.1.0版以来提供的协同程序支持。官方文件指出:

您可以将suspend Kotlin关键字添加到DAO方法中,以使 它们使用Kotlin协同路由功能实现异步。这确保了 它们不能在主线程上执行

你可以检查一下

因此,您应该在getCourse DAO方法中添加suspend关键字:

DAO接口

如果这样做,则无论您是否在主线程上发出请求,都可以返回结果:

存储库

视图模型

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()
    }
}
您还应该使用lifecycleScope,它在片段上可用,如您所见:

碎片


PS:考虑通过整个调用链传递所选的过程作为参数,而不是在您的回购中使用全局变量。

您应该利用自2.1.0版提供的CORUTION支持。官方文件指出:

您可以将suspend Kotlin关键字添加到DAO方法中,以使 它们使用Kotlin协同路由功能实现异步。这确保了 它们不能在主线程上执行

你可以检查一下

因此,您应该在getCourse DAO方法中添加suspend关键字:

DAO接口

如果这样做,则无论您是否在主线程上发出请求,都可以返回结果:

存储库

视图模型

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()
    }
}
您还应该使用lifecycleScope,它在片段上可用,如您所见:

碎片


PS:考虑通过整个调用链传递所选的过程作为参数,而不是在您的回购中使用全局变量。

我不能使用挂起函数,因为我是从我这里调用它的。fragment@Ajeeli您可以调用协同例程中的任何挂起函数。例如,在片段中,您可以调用viewModelOwner.viewModelScope.launch{/*call here*/}@Ajeeli,您无法在协同程序之外获取数据。viewModel.viewModelScope.launch{val cource=viewModel.getCourseData}由于调用它的callsite函数没有挂起,因此没有挂起。@Ajeeli您能否提供如何在协同程序之外使用该课程/希望如何使用它以进一步帮助您,请编辑您的问题。@Ajeeli更新了答案以适合您的用例!我不能使用挂起函数,因为我是从我的fragment@Ajeeli您可以调用协同例程中的任何挂起函数。例如,在片段中,您可以调用viewModelOwner.viewModelScope.launch{/*call here*/}@Ajeeli,您无法在协同程序之外获取数据。viewModel.viewModelScope.launch{val cource=viewModel.getCourseData}由于调用它的callsite函数没有挂起,因此没有挂起。@Ajeeli您能否提供如何在协同程序之外使用该课程/希望如何使用它以进一步帮助您,请编辑您的问题。@Ajeeli更新了答案以适合您的用例!谢谢你的意见。除了DAO之外,我实际上是在上面提到的函数上使用suspend。所以我添加了它,并按照您的建议使用了。。。viewLifecycleOwner.lifecycleScope,因为应用程序有时会崩溃。虽然@Animesh Sahu的答案是正确的,并且确实有效,但是,在某些情况下,应用程序崩溃,可能是因为某些DAO方法在主线程上运行。因此,我将接受这个完整的工作答案。谢谢你的投入。除了DAO之外,我实际上是在上面提到的函数上使用suspend。所以我添加了它,并按照您的建议使用了。。。viewLifecycleOwner.lifecycleScope,因为应用程序有时会崩溃。虽然@Animesh Sahu的答案是正确的,并且确实有效,但是,在某些情况下,应用程序崩溃,可能是因为某些DAO方法在主线程上运行。因此,我将接受这一完整的工作答案。