我越是在Kotlin中阅读和观看有关协程的报告,就越钦佩这种语言工具。 他们的稳定版本最近在Kotlin 1.3中发布,这意味着该是开始潜水的时候了,并以我现有的RxJava代码为例尝试协同程序。 在本文中,我们将重点介绍如何接受现有的网络请求并通过用协程替换RxJava来转换它们。

坦白说,在尝试协程之前,我认为它们与以前有很大不同。 但是,corutin的基本原理包括与RxJava反应流中所使用的相同的概念。 作为示例,让我们采用一个简单的RxJava配置来从我的一个应用程序创建网络请求:
- 使用Rx适配器(Retrofit2 :adapter-rxjava2 )定义用于Retrofit的网络接口。 函数将从Rx框架返回对象,例如Single或Observable 。 (这里使用函数而不是方法,因为假定旧代码也是用Kotlin编写的。那么,还是通过Android Studio从Java转换而来)。
- 我们从另一个类(例如,存储库或活动)中调用某个函数。
- 我们确定要在其上执行Scheduler的线程,并返回结果(方法.subscribeOn()和.observeOn() )。
- 我们保存到对象的链接以退订(例如,在CompositeObservable中)。
- 订阅事件流。
- 取消订阅流,具体取决于活动生命周期的事件。
这是使用Rx的基本算法(不考虑映射功能和其他数据操作的细节)。 至于corutin,原理变化不大。 相同的概念,只是术语在变化。
- 我们使用协程适配器定义用于Retrofit的网络接口。 函数将从Corutin API返回Deferred对象。
- 我们从另一个类(例如,存储库或活动)调用这些函数。 唯一的区别是:每个函数都应标记为suspend 。
- 定义将用于协程的调度程序。
- 我们保存到Job对象的链接以退订。
- 以任何可能的方式运行协程。
- 我们会根据活动生命周期的事件取消协程。
从上面的序列中可以看到,Rx和Corutin的执行过程非常相似。 如果我们不考虑实现细节,则意味着我们可以维持现有的方法-我们只替换一些东西以使实现实现协程友好。

我们必须采取的第一步是允许Retrofit返回Deferred对象。 递延对象是非阻塞期货,必要时可以撤消。 这些对象本质上是一个协程作业,其中包含相应作业的值。 使用Deferred类型可以使我们混合与Job相同的思想,并具有获取其他状态(例如成功或失败)的能力-这使其非常适合网络请求。
如果您将Retrofit与RxJava一起使用,则可能正在使用RxJava呼叫适配器工厂。 幸运的是,杰克·沃顿(Jake Worton)为她写了协程 。
我们可以在Retrofit构建器中使用此调用适配器,然后以与RxJava相同的方式实现Retrofit接口:
private fun makeService(okHttpClient: OkHttpClient): MyService { val retrofit = Retrofit.Builder() .baseUrl("some_api") .client(okHttpClient) .addCallAdapterFactory(CoroutineCallAdapterFactory()) .build() return retrofit.create(MyService::class.java) }
现在让我们看一下上面使用的MyService接口。 我们必须在Retrofit接口中用Deferred替换返回的Observable类型。 如果以前是这样的:
@GET("some_endpoint") fun getData(): Observable<List<MyData>>
现在我们将其替换为:
@GET("some_endpoint") fun getData(): Deferred<List<MyData>>
每次调用getData()时,Deferred对象都会返回给我们-网络请求的Job的模拟。 以前,我们以某种方式使用RxJava调用了此函数:
override fun getData(): Observable<List<MyData>> { return myService.getData() .map { result -> result.map { myDataMapper.mapFromRemote(it) } } }
在此RxJava流中,我们调用实用程序函数,然后将来自RxJava API的映射操作与从请求返回的数据的后续映射映射到UI层中使用的对象。 当我们使用带有协程的实现时,这会有所改变。 对于初学者,我们的函数必须挂起 (延迟),以便在函数体内进行延迟操作。 为此,还必须推迟调用函数。 延迟函数是非阻塞的,可以在初始调用后对其进行控制。 您可以启动,暂停,恢复或取消它。
override suspend fun getData(): List<MyData> { ... }
现在我们必须调用我们的效用函数。 乍一看,我们正在做同样的事情,但是我们必须记住,现在我们得到Deferred而不是Observable 。
override suspend fun getData(): List<MyData> { val result = myService.getData() ... }
由于此更改,我们不再能够使用RxJava API中的地图操作链。 即使到了这一刻,我们也无法获得数据-我们只有一个Deferred实例。 现在,我们必须使用await()函数来等待查询结果,然后继续执行该函数内的代码:
override suspend fun getData(): List<MyData> { val result = myService.getData().await() ... }
至此,我们获得了完整的请求以及其中的数据供使用。 因此,我们现在可以执行映射操作:
override suspend fun getData(): List<MyData> { val result = myService.getData().await() return result.map { myDataMapper.mapFromRemote(it) } }
我们将Retrofit接口与调用类一起使用,并使用了协程。 现在,我们要从“活动”或片段中调用此代码,并使用从网络获取的数据。
在我们的活动中,我们将首先创建一个指向Job的链接,在其中我们可以分配协程操作,然后使用它来控制例如在onDestroy()调用期间取消请求。
private var myJob: Job? = null override fun onDestroy() { myJob?.cancel() super.onDestroy() }
现在,我们可以为myJob变量分配一些内容。 让我们来看一下协程的请求:
myJob = CoroutineScope(Dispatchers.IO).launch { val result = repo.getLeagues() withContext(Dispatchers.Main) { //do something with result } }
在本文中,我不想深入研究Dispatchers或在协程中执行操作,因为这是其他文章的主题。 简而言之,这里发生了什么:
- 使用IO Dispatcher作为参数创建CoroutineScope实例。 该调度程序用于执行阻塞的I / O操作,例如网络请求。
- 我们使用启动功能启动协程-此功能启动新的协程并返回到Job类型变量的链接。
- 然后,我们使用指向我们存储库的链接通过执行网络请求来接收数据。
- 最后,我们使用Main调度程序在UI线程上进行工作。 在这里,我们可以向用户显示接收到的数据。
在下一篇文章中,作者承诺对细节进行更深入的研究,但是当前的材料应该足以开始研究协程。
在本文中,我们用Corutin API中的Deferred对象替换了Retrofit响应的RxJava实现。 我们称这些功能为从网络接收数据,然后在我们的活动中显示它们。 我希望您能看到需要多少更改才能开始使用协程,并希望该API的简单性,尤其是在读写代码时。
在原始帖子的评论中,我发现了一个传统要求:显示整个代码。 因此,我创建了一个简单的应用程序,该应用程序在启动时使用Yandex。Schedules API接收火车时刻表,并将其显示在RecyclerView中。 链接: https : //github.com/AndreySBer/RetrofitCoroutinesExample
我还想补充一点,协程似乎不是RxJava的次要替代品,因为协程不提供用于同步线程的等效操作集。 在这方面,值得研究Kotlin的ReactiveX实现: RxKotlin 。
如果您使用Android Jetpack,我还发现了Retrofit,协程,LiveData和MVVM的示例: https ://codinginfinite.com/kotlin-coroutine-call-adapter-retrofit/