Kotlin 协程使用记录,线性请求,并发请求等问题

·  阅读 1273
Kotlin 协程使用记录,线性请求,并发请求等问题

Kotlin的优点 1、空指针处理 2、扩展函数 3、高级函数 4、协程 5、Range 表达式 6、伴生对象

依赖

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1"
复制代码

1、协程的启动模式

在Kotlin协程当中,启动模式定义在一个枚举类中:

DEFAULT	默认的模式,立即执行协程体
LAZY	只有在需要的情况下运行
ATOMIC	立即执行协程体,但在开始运行之前无法取消
UNDISPATCHED	立即在当前线程执行协程体,直到第一个 suspend 调用

public enum class CoroutineStart {
    DEFAULT,
    LAZY,
    @ExperimentalCoroutinesApi
    ATOMIC,
    @ExperimentalCoroutinesApi
    UNDISPATCHED;
}
复制代码

开启一个协程lauch得到一个job

    Dispatchers.Main//挂载,同步主线程,更新UI
    Dispatchers.IO  //IO子协程
    Dispatchers.Default //CUP协程,处理较为复杂的绘制
    
    var job = GlobalScope.launch(Dispatchers.Main) {
    
    }
    
    job.cancel() // 取消一个协程
    job.cancelAndJoin() // 取消一个协程并且等待它结束
    job.join() // 等待直到子协程执行结束,结构化的并发
复制代码

切换协程执行的线程

Dispatchers.Main//挂载,同步主线程,更新UI
Dispatchers.IO  //IO子协程
Dispatchers.Default //CUP协程,处理较为复杂的绘制
//在子协程中切换执行的线程,一般用于更新View
withContext(Dispatchers.Main) {

}
复制代码

runBlocking

//使用runBlocking包裹起来的协程,里面不管开启多少个子协程,都在同一个线程中执行,自上往下执行,
 runBlocking(Dispatchers.IO) { 
            launch { 第一个被执行,都在同一个线程中 }
            launch { 第二个被执行,都在同一个线程中 }
        }
复制代码

delay、repeat

//delay非阻塞的函数,挂起,协程进入线程池空闲线程
//repeat循环
	GlobalScope.launch {
          //启动异步
          repeat(10) {
              //循环
              delay(1000) //挂起,协程进入线程池空闲线程
              Log.e("zxy", "$it")
          }
      }
复制代码

withTimeout和withTimeoutOrNull操作超时

runBlocking {
    withTimeout(1300L) {
        repeat(1000) { i ->
            Log.i("log","I'm sleeping $i ...")
            delay(500L)
        }
    }
}
复制代码

async和await

async可以支持并发,也可以支持串联通信,此时一般都跟await一起使用

	GlobalScope.launch {
            val result1 = GlobalScope.async {
                getResult1()
            }
            val result2 = GlobalScope.async {
                getResult2()
            }
            val result = result1.await() + result2.await()
            Log.e(TAG,"result = $result")
    }
复制代码

suspend:挂起函数

  1. 挂起函数只能在协程中和其他挂起函数中调用,不能在其他地方使用。
  2. suspend函数会将整个协程挂起,而不仅仅是这个suspend函数,也就是说一个协程中有多个挂起函数时,它们是顺序执行的。

Channel 协程间的通信(非阻塞、可挂起的)

//send挂起-类似于suspend
//使用Channel,必须关闭,否则接受者协程还在一直等待,不会执行挂起函数
runBlocking {
    val channel = Channel<Int>()//实例化通道
    launch {
        for (x in 1..5) channel.send(x * x)
        channel.close()//此处关闭通道
    }
    repeat(5) {
        //这里保证所有先前发送出去的元素都在通道关闭前被接收到。
        Log.e("zxy", "${channel.receive()}")
    }
}
复制代码

提供一个协程的扩展:

//使用方式:
 launchIOToMain({
            Log.e("zxy", "123")
            delay(6000)
            Log.e("zxy", "456")
            "123"
        }, {
            Log.e("zxy", "同步主线程")
        }, {
            Log.e("zxy", "异常")
        })
复制代码

扩展函数:


/**
 * 闭包使用:launchIO({异步},{挂起函数同步主线程},{异常返回,可以省略})
 *
 * @param block 异步协程,{可以实现delay()、repeat()、async()、await()、suspend()等}
 * @param callback 同步主协程,view更新
 * @param error 异常返回
 * @return 可操作协程 job.cancelAndJoin()
 */
fun <T> launchIOToMain(
    block:  suspend CoroutineScope.() -> T,
    callback:(T) -> Unit,
    error: ((Exception) -> Unit) = {}
): Job {
    return GlobalScope.launch {
        try {
            val data = withContext(Dispatchers.IO) { //协程切换,得到IO协程的泛型结果
                block()
            }
            withContext(Dispatchers.Main) {//协程切换主协程
                callback(data)
            }
        } catch (e: Exception) {
            withContext(Dispatchers.Main) {//异常
                error.invoke(e)
            }
        }
    }
}
复制代码

顺序执行异步操作1

//也可以使用队列操作
 GlobalScope.launch {
            repeat(maxTier) {
               GlobalScope.launch{
               	   //循环多个异步操作
               }
            }
            withContext(Dispatchers.Main) {
       		 //同步主线程,更新Ui
            }
        }
复制代码

顺序执行异步操作2

GlobalScope.launch {
            runBlocking {
                repeat(10) {
                    var job = GlobalScope.launch(Dispatchers.IO) {
                        Log.e("zxy", "$it")
                    }
                    delay(3000)
                    job.cancelAndJoin()//等待结束
                }
            }
        }
复制代码

总结:

1、线程的执行顺序:先执行当前协程里的非子协程语句,再执行当前协程的子协程的块,除非当前协程中只存在多个子协程

//输出结果:先得到45,再得到23
 GlobalScope.launch {
                var job = GlobalScope.launch(Dispatchers.IO) {
                    repeat(10) {
                        Log.e("zxy", "23")
                    }
                }
                Log.e("zxy", "45")
        }
复制代码

2、使用协程的时候需要考虑清楚,挂载协程的顺序,协程的执行优先级顺序,防止数据错乱

分类:
Android
标签: