Android Kotlin 使用协程取代回调函数

一、简述

如果说大家对于协程已经有些熟悉了,但是手拿一把未开封的 绝世好剑,却无法发挥真正的威力!或者大家不是很熟悉协程,还不清楚协程所带来的好处是啥?

这篇文章会为大家带来 通窍级别 的体验。

二、示例

2.1 普通写法

以获取一个 View 的宽高为例。

通常我们获取一个 View 的宽高,都是这么做的:

view.post {
    
    
	val height = view.measuredHeight
	val width = view.measuredWidth
}

因为至少需要等到 View 的布局流程走完,我们才能获得 View 的真实宽高。

那么接下来如何用协程来代替这个回调呢?各位请看:

2.2 协程写法

//	1. 创建一个 View 的扩展函数 await()
suspend fun View.await() = suspendCoroutine<View> {
    
     coroutine->
	this.post {
    
    
		//	2. 在 View.post 函数内,将本身返回
		coroutine.resume(this)
	}
}

使用(为了让大家看出效果,就放截图了):
使用
稍微的解释一下, launch() 是我自己对 GlobalScope.launch() 进行封装后的函数,这里可以看作直接开启了一个协程作用域。之后让 view 调用 await() 函数,返回的值就是宽高确定后的 View 啦。

三、带有成功和失败的回调

3.1 普通写法

我们以获取一个数据的成功或者失败为例:

fun getData(onSuccess:(String)->Unit,onFail:(Throwable)->Unit){
    
    
	Thread.sleep(1000)
	if ((Math.random() *10).toInt() % 2 == 0){
    
    
		onSuccess("数据")
	}else{
    
    
		onFail(IllegalStateException("获取数据失败"))
	}
}

使用:
使用

3.2 协程写法

suspend fun getData() = suspendCoroutine<String> {
    
    
    Thread.sleep(1000)
    if ((Math.random() *10).toInt() % 2 == 0){
    
    
        it.resume("数据")
    }else{
    
    
        it.resumeWithException(IllegalStateException("获取数据失败"))
    }
}

使用:

    GlobalScope.launch {
    
    
        val data = getData()
        //  <如果能够拿到数据,那就继续往下走>
        println("data:${
      
      data}")
    }

如果 getData() 能够成功返回数据,那么这样写是没问题的,但是 如果失败了,那就会抛出异常,所以我们必须要使用 try catch 语句

    GlobalScope.launch {
    
    
        try {
    
    
            val data = getData()
            //  <如果能够拿到数据,那就继续往下走>
            println("data:${
      
      data}")
        }catch (e:Throwable){
    
    
            //  <获取数据失败并且捕获异常>
        }
    }

对比一下之前的写法……是不是觉得还不如前面的写法呢?所以我们可以利用一下协程上下文——CoroutineConext 来搞搞事情……

3.3 使用协程上下文处理异常

  1. 我们先创建一个自定义的处理异常的上下文类:
abstract class NetErrorContext:AbstractCoroutineContextElement(NetErrorContext){
    
    

    companion object Key: CoroutineContext.Key<NetErrorContext>

    abstract fun handleException(throwable: Throwable)
}

这是很典型很模板式的写法……
表情

不懂的话也没关系,把这个模板记下来,以后有空的话写写关于协程上下文的文章。

  1. 然后我们让上下文去处理异常:
suspend fun getData() = suspendCoroutine<String> {
    
    
    Thread.sleep(1000)
    if ((Math.random() *10).toInt() % 2 == 0){
    
    
        it.resume("数据")
    }else{
    
    
        //it.resumeWithException(IllegalStateException("获取数据失败"))
        it.context[NetErrorContext]?.handleException(IllegalStateException("获取数据失败"))
    }
}
  1. 定义一个异常处理对象:
    val netErrorHandler = object :NetErrorContext(){
    
    
        override fun handleException(throwable: Throwable) {
    
    
            println("网络请求异常……")
        }
    }
  1. 最后在开启协程体的时候,将上下文加入:
    GlobalScope.launch(context = netErrorHandler) {
    
    
        val data = getData()
        //  <如果能够拿到数据,那就继续往下走>
        println("data:${
      
      data}")
    }

这样的话,如果有异常就会被 netErrorHandler 捕获到。而且又回到这朴实无华的写法了……

当然,你们自己自定义上下文的时候,完全可以根据自己的业务去实现一些方法,上面的例子仅仅是定义了一个 handleException() 方法,并且也只定义了一个参数,在实际开发过程中,这样还是很具有灵活性的……而且协程上下文是可以叠加的……

猜你喜欢

转载自blog.csdn.net/catzifeng/article/details/109262842