【问题标题】:Kotlin Coroutines how to achieve to call api in right wayKotlin Coroutines 如何实现以正确的方式调用 api
【发布时间】:2021-10-13 04:35:34
【问题描述】:

嘿,我想从 object 类调用 api。我是协程的新手。我尝试了一些代码,但我不确定它是否正确。

  1. LoginHelper里面有一个叫做logout的功能不止一个功能。我想先执行 api 调用。然后我想在注销中执行其他功能。

  2. Mainactivity 我正在调用 LoginHelper.logout 它将完成然后我需要执行其他行。但我不想做暂停功能,因为它也在使用其他地方。

我也遇到了错误进程:

com.dimen.app, PID: 12496
    android.os.NetworkOnMainThreadException
        at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1605)

Session.kt

interface Session{
    @DELETE("/session/delete")
    fun deleteSession(): Call<Void>
}

SessionRepository.kt

suspend fun deleteSession(): RequestResult<Void> {
        return apiCall(api.deleteSession())
}

RequestResult 是一个Sealed Class

sealed class RequestResult<out T : Any> {
    data class Success<out T : Any>(): RequestResult<T>
    data class Error(): RequestResult<Nothing>()
    fun result(success: (data: T?) -> Unit),error: (error: Error) -> Unit)
}

MainActivity.kt

private fun setupLogout() {
    logoutButton.setOnClickListener {
        LoginHelper.logout() // need to wait untill this finish
        // more logic here....
    }
}

LoginHelper.kt

object LoginHelper {

    fun logout() {
        logD("logout")
        deleteSession() // need to wait untill this finish and then excute more function....
    } 

    private fun deleteSession() {
       runBlocking{
        apiCall.deleteSession().execute()
       }
    }
}

【问题讨论】:

  • apiCall.deleteSession(it) 究竟返回了什么?
  • 只有执行才会返回 Void
  • 但它是什么类型的?它是否同步执行一项长期任务? getToken()deleteSession() 是挂起函数吗?
  • @Tenfour04 getToken 是返回字符串的普通函数。 deleteSession() 是 api 调用并返回 RequestResult 它是一个密封的类。 Sealed Class
  • 那么apiCall.deleteSession()启动异步请求并立即返回?如果是这种情况,您根本不需要协程。

标签: android kotlin async-await kotlin-coroutines coroutine


【解决方案1】:

除非您确切知道自己在做什么,否则切勿在 Android 应用中使用 runBlocking。这是 99% 的错误选择,因为它违背了使用协程的目的。阻塞意味着当前线程等待协程运行其异步代码。但是你不能阻塞主线程,因为这会冻结 UI。

由于您的 LoginHelper 是 object 或单例,因此如果要启动协程,它需要自己的 CoroutineScope。

您可以将deleteSession() 设为挂起函数,以便它可以调用api.deleteSession() 挂起函数。

您可以让logout() 启动一个协程以顺序删除会话并随后执行其他任务。您可以让它返回已启动的 Job,以便其他类可以选择是简单地启动注销,还是在协程中启动并等待注销。

object LoginHelper {
    private val scope = CoroutineScope(SupervisorJob() + CoroutineName("LoginHelper"))

    fun logout(): Job = scope.launch {
        logD("logout")
        deleteSession()
        // .... more functions that happen after deleteSession() is complete
    }

    private suspend fun deleteSession() {
        Tokenclass.getToken()?.let {
            logE("token ::-> $it")
            apiCall.deleteSession(it).execute()
        }
    }
}

如果你想让外部类能够等待logout完成,它可以在自己的协程中对返回的Job调用join(),例如:

logoutButton.setOnClickListener {
    lifecycleScope.launch {
        LoginHelper.logout().join()
        // more logic here....
    }
}

如果不需要在activity中等待,就不需要启动协程,也不需要调用join()

【讨论】:

  • 你能看一下图片吗?它在logout 函数上给出错误。我不想创建logout 函数来suspend它会影响其他地方
  • 你添加了// .... more functions. These will probably occur before Executor is done.这一行。如果我想先执行 deleteSession 函数,然后再执行其他函数。我们怎样才能做到这一点。
  • 嘿@Tenfour04 我完全更新了我正在使用的问题。你能检查一下吗?我正在尝试做什么以及返回类型deleteSession()
  • 嘿@Tenfour04 成功了,谢谢你这么粘
  • 有点怀疑 @Tanfour04 SupervisorJobDispatchers.IO/MAIN?DEFAULT 的 d/w 是什么。
猜你喜欢
  • 2017-08-25
  • 2020-12-14
  • 1970-01-01
  • 2015-11-08
  • 1970-01-01
  • 1970-01-01
  • 2018-07-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多