【Kotlin协程】基于RxJava项目的Coroutine改造

【Kotlin协程】基于RxJava项目的Coroutine改造_第1张图片最近,Android宣布彻底废弃AsyncTask,推荐Coroutine作为首选的异步编程方案。

如果说AsyncTask被Coroutine替代毫无悬念,那RxJava与Coroutine如何取舍呢?
【Kotlin协程】基于RxJava项目的Coroutine改造_第2张图片
J神很早就就给出自己的看法了。当时Flow还未出现,现在J神恐怕连ObservableSubject也早就不用了。 RxJava在Android中被废弃的日子恐怕也不远了。

那么,一个既有的RxJava的MVVM项目如何简单地改造为Coroutine呢?

RxJava例子


以一个RxJava项目为例,Model层基于Single进行数据请求
【Kotlin协程】基于RxJava项目的Coroutine改造_第3张图片

class Api {
  fun fetchPersons(): Single<List<Person>> = ...
}
class ViewModel(
    private val api: Api = Api(),
    private val scheduler: Scheduler = Schedulers.io()
) {
  private val mutablePersons = MutableLiveData<List<Person>>()
  val persons: LiveData<List<Person>> = mutablePersons

  fun onCreate() {
    api.fetchPersons()
        .subscribeOn(scheduler)
        .subscribe(mutablePersons::postValue)
  }
}

kotlinx-coroutines-rx2


kotlinx-coroutines-rx2 是个好东西,可以帮助我们在Rx2与Coroutine之间任意切换。

除了Rx2以外,Rx3等其他Reactive库也有支持https://github.com/Kotlin/kotlinx.coroutines/tree/master/reactive

Coroutine => Rx2

【Kotlin协程】基于RxJava项目的Coroutine改造_第4张图片

Rx2 => Coroutine

【Kotlin协程】基于RxJava项目的Coroutine改造_第5张图片
使用SingleSource.await,可以将上面例子中Model层的Single转换为suspend函数
【Kotlin协程】基于RxJava项目的Coroutine改造_第6张图片

class ViewModel(
    private val api: Api = Api(),
    private val coroutineContext: CoroutineContext = CommonPool
) {
  private val mutablePersons = MutableLiveData<List<Person>>()
  val persons: LiveData<List<Person>> = mutablePersons

  fun onCreate() {
    launch(coroutineContext) {
      //将Rx请求转换挂起函数
      val persons = api.fetchPersons().await()
      mutablePersons.postValue(persons)
    }
  }
}

更多注意事项


虽然例子中几乎毫不费力地将Rx转换为了Coroutine,但若要用在实际项目中,可能还要稍微加工一下:

class PersonsViewModel(
    private val api: Api = Api(),
    coroutineContext: CoroutineContext = CommonPool
) : ViewModel() {
  private val compositeJob = Job() //1
  private val mutablePersons = MutableLiveData<List<Person>>()
  val persons: LiveData<List<Person>> = mutablePersons

  init {
    launch(coroutineContext, parent = compositeJob) {
      try { //2
        val persons = api.fetchPersons().await()
        mutablePersons.postValue(persons)
      } catch (e: Exception) {
        ...
      }
    }
  }

  override fun onCleared() {
    compositeJob.cancel() 
  }
  1. 启动协程时指定的父Job,在onCleared时统一cancel,避免泄露。当然这个工作现在可以通过ViewModel的KTX扩展来做
  2. launch中通过try...catch...替代Rx的onError捕获异常,关于Coroutine的异常捕获的更多内容可参考Kotlin Coroutine 最佳实践

与RxJava共享Schedulers


使用Scheduler.asCoroutineDispatcher可以将RxJava的Schedulers转为Dispatcher,让重构前后的线程池不变,保证一致性。

object AppCoroutineDispatchers(
    val io: CoroutineContext = Schedulers.io().asCoroutineDispatcher(),
    val computation: CoroutineContext = Schedulers.computation().asCoroutineDispatcher(),
    val ui: CoroutineContext = UI
)

//use
launch(AppCoroutineDispatchers.io) { ... }

最后看一波反向操作


虽然Android更推荐使用Coroutine,但是kotlinx-coroutines-rx2 还是很友好地提供了Coroutine转Rx2的API,所以顺道看一下如何进行反向操作:

使用rxSingle将挂起函数转为Single
【Kotlin协程】基于RxJava项目的Coroutine改造_第7张图片

class Api {
  suspend fun fetchPersons(): List<Person> = ...
}
class ViewModel(
    private val api: Api = Api(),
    private val scheduler: Scheduler = Schedulers.io(),
    val coroutineContext: CoroutineContext = CommonPool
) {
  private val mutablePersons = MutableLiveData<List<Person>>()
  val persons: LiveData<List<Person>> = mutablePersons

  fun onCreate() {
    rxSingle(coroutineContext) { api.fetchPersons() }
        .subscribeOn(scheduler)
        .subscribe(mutablePersons::postValue)
  }
}

你可能感兴趣的:(#,Kotlin,Coroutine,Kotlin,RxJava,安卓,协程,RxJava,Coroutine,Kotlin)