Kotlin 协程之三:Android中的应用

系列文章:
Kotlin 协程之一:基础使用
Kotlin 协程之二:原理剖析
Kotlin 协程之三:Android中的应用

至此,我们完全了解了协程的使用和实现原理,那么如何应用到我们Android项目中呢?这里讲一些简单的例子。

1.将线程改为协程

通常我们有一些小的延时任务或异步任务,会使用线程池来解决,这里我们可以用协程来解决,比如:在页面创建后,异步读取数据库中的内容,然后展示到TextView上。

CoroutineScope(Dispatchers.Main).launch {
  val data = withContext(Dispatchers.IO){ //get data from DB }
  textView.text = data
}

2.将网络请求改为协程

通常我们的网络请求都是使用的第三方库,如retrofit等,他们的共同点都是结果需要通过Callback回调,这显然是我们不愿意看到的,这里我们可以改为协程实现。

//假如这是网络库的Call对象
private class Call(data: String = "") {
  fun addCallback(cb: Callback) {
    //register callback
  }
}
private interface Callback {
  fun onCall(res: String)
  fun onError(error: Exception)
}

假如这是网络库的Call对象,我们通过其来添加Callback,那么我们为他增加一个扩展函数。

private suspend fun Call.await(): String = suspendCancellableCoroutine {
  addCallback(object : Callback {
    //请求异常时调用resumeWithException恢复协程
    override fun onError(error: Exception) {
      it.resumeWithException(error)
    }

    //请求成功时调用resume恢复协程
    override fun onCall(res: String) {
      it.resume(res)
    }
  })
}

这样一来,我们的请求和处理就可以写成协程的样子,甚至多个有依赖关系的接口也支持:

CoroutineScope(Dispatchers.Main).launch {
  val data1 = Call().await()
  val data2 = Call(data1).await()
  textView.text = data2   
}

这样一来,我们大量回调嵌套的异步任务就清晰了很多。

3.生命周期

以上都是我们如何使用协程完成一个任务,那么对于应用来说,还要考虑的是声明周期,就比如网络请求在页面退出时应该及时cancel,有效避免资源浪费、崩溃和内存泄露。

open class BaseActivity : AppCompatActivity() {

    private val scope = CoroutineScope(Dispatchers.Main)
    
    fun startCoroutine(context: CoroutineContext = EmptyCoroutineContext,
                       start: CoroutineStart = CoroutineStart.DEFAULT,
                       block: suspend CoroutineScope.() -> Unit): Job = scope.launch(context, start, block)

    override fun onDestroy() {
        super.onDestroy()
        scope.cancel()
    }
}

首先我们可以在基类创建一个scope,每个页面一个单独的scope;然后在onDestroy()进行scope的cancel,相当于取消掉当前页面所有未完成的协程;最后实现一个开启协程的方法,保证我们的子类开启协程时用的都是该scope的。

class MainActivity : BaseActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        startCoroutine(Dispatchers.Main) {
            showLoadingView()
            val data1 = Call().await()
            val data2 = Call(data1).await()
            displayData(data2)
        }
    }
}  

在子类里就可以正常使用了,并且不用关注cancel事件。


版权声明:本文为qq_15827013原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。