前言
随着Kotlin的蓬勃发展,在StackOverflow网站的统计中,最受开发者欢迎的编程语言排行榜,Kotlin得到了72.6%的高比例支持。
Kotlin1.3协程正式发布,这意味着自Kotlin1.3起,协程的语言支持与API已完全稳定。
协程简介
协程官方定义
协程通过将复杂性放入库来简化异步编程。程序的逻辑可以在协程中顺序地表达,而底层库会为我们解决其异步性。该库可以将用户代码的相关部分包装为回调、订阅相关事件、在不同线程(甚至不同机器)上调度执行,而代码则保持如同顺序执行一样简单。
协程相对于线程的优点:
在Android中新建一个线程,大约需要消耗1M的内存,如果使用线程池,线程间数据的同步是一个非长复杂的事情,所以就有了协程:
- 可以看作是轻量级线程,创建一个协程的成本很低
- 可以轻松的挂起和恢复操作
- 支持阻塞线程的协程和不阻塞线程的协程
- 可以更好的实现异步和并发
启动十万个协程,并且在一秒后,每个协程都输出一个点。现在尝试使用线程来实现,会发生什么?(很可能你的代码会产生某种内存不足的错误)
import kotlinx.coroutines.*
fun main() = runBlocking {
repeat(100_000) { // 启动大量的协程
launch {
delay(1000L)
print(".")
}
}
}
简单的理解就是,可以极大程度的简化异步操作,可以顺序地表达程序,协程也提供了一种避免阻塞线程并用更廉价、更可控的操作替代线程阻塞的方法 – 协程挂起。。
协程在Android中的使用
Android中使用Kotlin协程,需引入kotlinx.coroutines库:
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:x.x.x'
//kotlinx-coroutines-android 库中引入了 kotlinx-coroutines-core 和 kotlin-stdlib两个库
kotlin-stdlib:1.2M 使用Kotlin的项目必须引入的库
kotlinx-coroutines-core: 1.5M
kotlinx-coroutines-android: 21K
所以在我们现在使用Kotlin开发的Android项目中引入kotlinx-coroutines-android库,项目体积会增加 1.5M左右,生成APK后体积约增加600K
应用
常见场景:
在后台线程执行一个复杂任务,下一个任务依赖于上一个任务的执行结果,所以必须等待上一个任务执行完成后才能开始执行。看下面代码中的三个函数,后两个函数都依赖于前一个函数的执行结果
fun requestToken(): Token {
// makes request for a token & waits
return token // returns result when received
}
fun createPost(token: Token, item: Item): Post {
// sends item to the server & waits
return post // returns resulting post
}
fun processPost(post: Post) {
// does some local processing of result
}
常见的做法是使用回调,把之后需要执行的任务封装为回调。
fun requestTokenAsync(cb: (Token) -> Unit) { ... }
fun createPostAsync(token: Token, item: Item, cb: (Post) -> Unit) { ... }
fun processPost(post: Post) { ... }
fun postItem(item: Item) {
requestTokenAsync { token ->
createPostAsync(token, item) { post ->
processPost(post)
}
}
回调在只有两个任务的场景是非常简单实用的,很多网络请求框架的 onSuccess Listener 就是使用回调,但是在三个以上任务的场景中就会出现多层回调嵌套的问题,而且不方便处理异常。
使用Kotlin协程
suspend fun requestToken(): Token { ... } // 挂起函数
suspend fun createPost(token: Token, item: Item): Post { ... } // 挂起函数
fun processPost(post: Post) { ... }
fun postItem(item: Item) {
GlobalScope.launch {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
// 需要异常处理,直接加上 try/catch 语句即可
}
}
网络请求框架Retrofit已在V2.6.0(2019-06-05)正式支持协程中的suspend关键字
@GET("users/{id}")
suspend fun user(@Path("id") id: Long): User
//获取网络数据
val user:User = Api.getUser("id")
ps:本文只做简单的协程介绍,具体使用可参考官方文档等