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"
可以绑定activity生命周期的协程
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'//lifecycleScope
推荐使用 lifecycleScope 的协程,使用是一样的
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:挂起函数
- 挂起函数只能在协程中和其他挂起函数中调用,不能在其他地方使用。
- 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、使用协程的时候需要考虑清楚,挂载协程的顺序,协程的执行优先级顺序,防止数据错乱