我有一个使用网络获得companyName.的异步代码,这个companyName在另一个方法调用中使用.我不确定Android目前如何正确地等待异步调用完成,然后再继续其他代码.我看到自己在做一份工作

val job = launch {
   // async code 
   companyName = getCompanyName(stock)
}
job.join()
// Do stuff with companyName

但这似乎不再是范例,因为我收到一个错误,不使用CoroutineScope.,那么在继续其他代码之前,我如何等待协程程序完成运行(而不使用runBlock{})?

以下是我的代码:

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewModel.currentStock.observe(viewLifecycleOwner) { stock ->
            // TODO: change this to be company name
            var companyName = ""
            lifecycleScope.launch {
                withContext(Dispatchers.IO) {
                    companyName = getCompanyName(stock)
                }
            }
            deleteTable()
            getData(companyName)  // RELIES ON COMPANYNAME
        }
    }

getData(companyName)的定义为

 private suspend fun finraGetCfruid(companyName: String): String {
        val url = "https://www.finra.org/finra-data/fixed-income/corp-and-agency"

        val queue = Volley.newRequestQueue(requireActivity())
        var cfruid = ""

        val stringRequest = object: StringRequest(Request.Method.GET, url,
            {
            },
            { error ->
                Log.e("Error", error.toString())
            })
        {
            override fun  parseNetworkResponse(response : NetworkResponse) : Response<String> {
                val cookies = HttpCookie.parse(response.headers?.get("Set-Cookie"))
                val indexOfEqualSign = cookies[0].toString().indexOf('=')
                cfruid = cookies[0].toString().substring(indexOfEqualSign + 1)

                datainterface.getMoreData(companyName, cfruid)

                return super.parseNetworkResponse(response)
            }
        }

        queue.add(stringRequest)

        return cfruid
    }

推荐答案

将发生在协程中的代码移入其中.协程(launch Lambda)中的代码是同步的.启动的协程本身与外部的代码是异步的.

而且,假设您使用这个协程来处理视图,您需要使用viewLifecycleOwner‘S lifecycleScope,而不是片段的.(如果您不是在这个协程中处理视图,那么它可能从一开始就不应该在片段中.)

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    viewModel.currentStock.observe(viewLifecycleOwner) { stock ->
        viewLifecycleOwner.lifecycleScope.launch {
            val companyName = withContext(Dispatchers.IO) {
                getCompanyName(stock)
            }
            deleteTable()
            getData(companyName) 
        }
    }
}

如果getCompanyName是暂停功能(并正确实现),则不需要withContext:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    viewModel.currentStock.observe(viewLifecycleOwner) { stock ->
        viewLifecycleOwner.lifecycleScope.launch {
            val companyName = getCompanyName(stock)
            deleteTable()
            getData(companyName) 
        }
    }
}

对于嵌套/重复较少的代码,您可以考虑使用中间流.这也将防止可能的竞争条件.如果两个值连续快速到达,则从LiveData观察器启动的协同 routine 可能会并行运行,这可能会导致错误,具体取决于您在该协同 routine 中所做的操作.但流运算符是按顺序处理的,因此不会发生这种情况.根据您具体在做什么,您可能会进一步优化这一点,以避免将时间浪费在过时的项目上,将onEach替换为transformLatest.这将导致它在新项目快速到达时提前取消对前一项目所做的工作.

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    viewModel.currentStock.asFlow()
        .onEach { stock ->
            val companyName = getCompanyName(stock)
            deleteTable()
            getData(companyName) 
        }
        .launchIn(viewLifecycleOwner.lifecycleScope)
}

我不熟悉Volley,只是简单地浏览了一下文档.在我看来,您正在使用StringRequest子类直接处理被覆盖的parseNetworkResponse()函数中的响应.因此,要将其转换为挂起函数,我认为它应该如下所示:

suspend fun RequestQueue.awaitNetworkResponse(method: Int, url: String): NetworkResponse = suspendCancellableCoroutine { continuation ->
    val request = object: StringRequest(
        method,
        url,
        {},
        { continuation.resumeWithException(it) }
    ) {
         override fun  parseNetworkResponse(response : NetworkResponse) : Response<String> {
             continuation.resume(response)
             return super.parseNetworkResponse(response)
         }
    }
    add(request)
    continuation.invokeOnCancellation {
        request.cancel()
    }
}

现在,您可以在协程中同步使用此函数:

private suspend fun finraGetCfruid(companyName: String): String {
    val url = "https://www.finra.org/finra-data/fixed-income/corp-and-agency"

    val queue = Volley.newRequestQueue(requireActivity())
    val response = try {
        queue.awaitNetworkResponse(Request.Method.GET, url)
    } catch (e: VolleyError) {
        Log.e("Error", error.toString())
        return "" // or some other behavior?
    }
    val cookies = HttpCookie.parse(response.headers?.get("Set-Cookie"))
    val indexOfEqualSign = cookies[0].toString().indexOf('=')
    val cfruid = cookies[0].toString().substring(indexOfEqualSign + 1)
    datainterface.getMoreData(companyName, cfruid)
    return cfruid
}

Android相关问答推荐

为什么使用. DeliverveAsState()时会出现空指针异常?

derivedStateOf与使用key和MutableState记住

组成底部导航栏,自定义形状,周围透明

判断文本视图是否为单行

在Jetpack Compose中,我可以配置动画以恒定的速度而不是恒定的时间运行吗?

当提供非状态对象时,compose 如何进行重组

Android Drawable文件夹中的图像显示模糊

Material 3 中的 ModalBottomSheet 用于 compose

Kotlin Multiplatform Mobile targetSdk 已弃用

React Native Android 应用程序在调试模式下运行良好,但当我们发布 apk 时,它会生成旧版本的应用程序

在 Jetpack Compose 的无状态 Compose 中管理条件逻辑

如何在 JetpackCompose 的 LazyColumn 中 Select 多个项目

组成不重叠的元素

如何解决 compose 中material 图标的由于接收器类型不匹配,以下候选者都不适用

java.lang.String 类型的值 Forbidden 无法转换为 JSONObject

如何在 Dolphin 中启用 android studio new logcat | 2021.3.1 金丝雀 6?

Jetpack Compose Material3 - switch 标签

如何让用户与任意应用程序共享文件?

Android TTS 在屏幕关闭一段时间后停止朗读

单个用户可以在 Firebase 身份验证中将多个电话号码链接到他的帐户吗?