【问题标题】:Kotlin Coroutines in Android ServiceAndroid 服务中的 Kotlin 协程
【发布时间】:2018-02-07 07:00:00
【问题描述】:

我有一个 Android 服务,它在服务器在线时启动并与服务器同步不同类型的数据。 我是 Kotlin 协程的新手,我正在尝试完成以下工作:

fun syncData{
//Job1 make retrofit call to server
//Job2 make retrofit call to server after job1 is done.
//Job3 make retrofit call to server after job2 is done and so on. 
//After all jobs are done I'll stop service.
}

我正在关注这个帖子: Kotlin Coroutines the right way in Android

这让我想到了这个解决方案:

fun syncData() = async(CommonPool){
    try{
        val sync1 = async(CommonPool){
            job1.sync()
        }

        val sync2 = async(CommonPool){
            job2.sync()
        }

        val sync3 = async(CommonPool){
            job3.sync()
        }

        val sync4 = async(CommonPool){
            job4.sync()
        }

        job1.await()
        job2.await()
        job3.await()
        job4.await()
    }catch (e: Exception){
    }finally {
        stopSelf()
    }
}

但是当我在 logcat 上获得改造的日志时,每次调用都是混合的。来自 job3 的调用在 job1 之前,以此类推。 如何在管道中执行它们?我有点迷失在 Kotlin 的协程中,所以我不知道具体如何实现。

【问题讨论】:

  • 您能否澄清一下job1job2 等变量的类型以及它们上的sync() 函数到底在做什么?
  • 问题解决了吗?
  • 是的,有点。我仍然使用协程,因为我最终不需要等待每个结果。所以效果很好。但主要问题是我列出的每个作业都是一个改造调用,这是一个异步调用,但我想等待每个调用结束,这就是为什么我认为 async await 可以为我提供解决方案。

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


【解决方案1】:

如果您必须一个接一个地执行它们,并且就像你说的那样,它们依赖彼此,这不是并发的有效用例执行,就像您在这里使用协程一样。 那就按顺序做吧。

在您的示例中,您将一次分派所有任务并等待它们完成。哪个先执行完全随机。

如果 Job1、Job2 和 Job3,彼此独立并且可以并行运行,那么它会起作用。一个协程,最后等待每个完成。你会不关心执行顺序

这些想法独立于协程,适用于每种并发编程风格。

【讨论】:

    【解决方案2】:

    你可以给每个sync()添加惰性。

    只需将实现更改为:

    fun syncData() = async(CommonPool){
            try{
                val sync1 = async(coroutineContext, start = CoroutineStart.LAZY){
                    job1.sync()
                }
    
                val sync2 = async(coroutineContext, start = CoroutineStart.LAZY){
                    job2.sync()
                }
    
    
                val sync3 = async(coroutineContext, start = CoroutineStart.LAZY){
                    job3.sync()
                }
    
                val sync4 = async(coroutineContext, start = CoroutineStart.LAZY){
                    job4.sync()
                }
                job1.await()
                job2.await()
                job3.await()
                job4.await()
            }catch (e: Exception){
            }finally {
                stopSelf()
            }
        }
    

    每个作业都不会在作业执行之前执行。

    看看coroutines guide

    【讨论】:

    • coroutineContext 只是使用与其父协程上下文相同的上下文,在这种情况下是相同的。
    【解决方案3】:

    同意@s1m0nw1和他的answer

    这太难接近了。您所需要的就这么简单:

    val job = SupervisorJob()
    
    fun syncData() = launch(job) {
        try { 
            val result1 = makeRetrofitCall1()
            val result2 = makeRetrofitCall2(result1)
            ...
            val resultN = makeRetrofitCallN(resultNMinusOne)
        } catch(e: Exception) {
            // handle exception
        } finally {
            stopSelf()
        }
    }
    
    override fun onDestroy() {
        super.onDestroy()
        job.cancel()
    }
    
    

    这里makeRetrofitCall 表示直接API 调用为retrofitClient.getData() 其中retrofitClient

    interface RetrofitClient {
    
        @GET("endpoint")
        suspend fun getData(): MyCustomResult
    }
    

    【讨论】:

    • 我也会和launch(Dispatchers.IO+job)一起启动协程
    • @Geoffrey 和我一样。但是如果你想传递数据来查看和避免ViewRoot$CalledFromWrongThreadException,你应该使用 MainThread 而不是 IO
    • 在这种情况下,makeRetrofitCallX() 函数必须调度。
    猜你喜欢
    • 1970-01-01
    • 2020-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-21
    • 2020-10-07
    相关资源
    最近更新 更多