【问题标题】:Suspend function 'callGetApi' should be called only from a coroutine or another suspend function挂起函数“callGetApi”只能从协程或另一个挂起函数中调用
【发布时间】:2019-05-24 12:23:35
【问题描述】:

我正在从 onCreate(...) 调用挂起函数

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    ...
    callGetApi()
}

而挂起的功能是:-

suspend fun callGetApi() {....}

但出现了错误暂停函数“callGetApi”只能从协程或其他暂停函数中调用

【问题讨论】:

    标签: android kotlin kotlin-coroutines coroutine coroutinescope


    【解决方案1】:

    暂停函数只能从协程中调用。这意味着您需要使用协程构建器,例如launch。例如:

    class Activity : AppCompatActivity(), CoroutineScope {
        private var job: Job = Job()
    
        override val coroutineContext: CoroutineContext
            get() = Dispatchers.Main + job
    
        override fun onDestroy() {
            super.onDestroy()
            job.cancel()
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            launch {
                val result =  callGetApi()
                onResult(result) // onResult is called on the main thread
            }
        }
    
        suspend fun callGetApi(): String {...}
    
        fun onResult(result: String) {...}
    }
    

    要在 Android 中使用 Dispatchers.Main,请将依赖项添加到应用的 build.gradle 文件中:

    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.1'
    

    更好的方法是在ViewModelActivity/Fragment 中使用扩展属性:

    它附加到Activity/Fragment 的生命周期,并在它们销毁时取消启动的协程。

    【讨论】:

    • 我没有看到任何文档提到 async-await 是一种反模式。 async-await的主要用途是在不同线程中并行运行任务,但我们也可以在后台线程中运行一个任务,使用async,同时做一些其他的事情,然后使用await.等待结果
    • 这是另一个并行分解的例子,但是你写了async { call() }.await(),这是一个反模式,因为:1)没有并发,2)如果call()失败,它会导致你的顶层launched 协程被立即取消(你没有机会进行常规异常处理),3)它比 withContext() 更重量级,它重用了相同的协程。
    • 同意,在这种情况下async-await 是一种滥用。
    • @IgorGanapolsky 请确保您正在实现CoroutineScope 接口:class Activity : AppCompatActivity(), CoroutineScope
    • @Sergey 我们需要lifecycleScope api 用于launch 在Activity 中。以及对 androidx.lifecycle:lifecycle-runtime-ktx: 的 gradle 依赖
    【解决方案2】:

    截至 2019 年 7 月,看起来最优雅的方式是here

    import androidx.lifecycle.lifecycleScope
    import kotlinx.coroutines.launch
    
    class Activity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super...
    
            lifecycleScope.launch {
                val result =  callGetApi()
                onResult(result) 
            }
        }
    }
    

    不要忘记添加相应的库:

    implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-alpha02"
    

    【讨论】:

    • @IgorGanapolsky 您是否在答案底部添加了相应的库?
    • 我遇到了崩溃
    【解决方案3】:

    上面的答案有效,但我解决了它而没有继承CoroutineScope 类,只使用...。 gradle.build

      dependencies {
          implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3'
      }
    

    活动.kt

      import kotlinx.coroutines.GlobalScope
      import kotlinx.coroutines.Dispatchers
    
      GlobalScope.launch (Dispatchers.Main) { callGetApi() }
    

    Dispatchers.Main 很重要,因为您无法在除主线程之外的任何其他线程中更新 UI。

    但建议继承 CoroutineScope 来维护 Activity 的生命周期和 onDestroy 的 Activity 杀死作业

    【讨论】:

    • 是的,乍一看这似乎对您有用,但它直接与recommendations in the documentation 背道而驰。在 Android 上使用这种风格会使您的应用程序出错并导致奇怪的崩溃。
    • 根据文档GlobalScope.launch 不建议使用。使用本地范围有可能取消协程的最佳实践。
    • 即使我提到线程它会崩溃吗.. 即Dispatchers.Main 而不是像Dispatchers.Default, Dispatchers.IO, Dispatchers.Unconfined @MarkoTopolnik @Sergey 这样的其他人
    • 这是不同的崩溃来源。您的活动可能会消失,但协程将继续存在。然后它将在稍后完成并尝试更新死活动。 CoroutineScope 是关于控制协程的生命周期,并确保它不会在其结果的接收者消失后继续在后台执行。
    • 这与 Kotlin 1.3.60 不兼容
    猜你喜欢
    • 2020-11-07
    • 1970-01-01
    • 2021-11-03
    • 2021-06-05
    • 2020-11-18
    • 2020-06-27
    • 1970-01-01
    • 2019-08-01
    相关资源
    最近更新 更多