参考文章:
https://kaixue.io/kotlin-coroutines-1/
https://johnnyshieh.me/posts/kotlin-coroutine-introduction/
kotlin协程这个概念不是一天两天提出的了,有兴趣想学一学,但是在网上你可能会看到很多非常专业的术语:
协程和线程类似;就像一种轻量级的线程;是协作式的,不需要线程的同步操作;协程是用户态的,他的切换不需要和操作系统交互............
等等一些话。
讲道理,看完这些话仿佛看到了一个超越自己认知的新东西,这些话在其他的语言中可能是对的,但是和kotlin中的协程概念不同:
kotlin协程是一个官方提供的线程调度的api,使用kotlin协程,可以用看似同步的方式,写出异步的代码。
CoroutineScope:协程对象本身,可直接通过CoroutineScope构造方法new对象,或使用GlobalScope全局协程对象,或继承自定义CoroutineScope(库中提供了CoroutineScope的实现类,在之后有介绍)
CoroutineContext:协程上下文,主要由协程的Job和CoroutineDispatcher组成;CoroutineContext作为CoroutineScope的参数使用。
CoroutineDispatcher:协程调度器,通过该变量可指定协程所运行的线程。
Dispatchers.Main //指定任务运行在主线程
Dispatchers.IO //指定任务运行在子线程,例如网络请求,文件读取
Dispatchers.Default //未指定Dispatchers时,有JVM的共享线程池支持
创建协程常用的方法主要有launch和async(runBlocking官方注释中说道该方法会阻塞当前线程,直到使用runBlocking启动的协程运行结束才会恢复当前线程,一般用于main方法和test测试中,之后就不提他的使用了)
使用launch手动创建一个协程:
CoroutineScope(Dispatchers.Main).launch { //new CoroutineScope对象,并创建协程
}
GlobalScope.launch { //使用GlobalScop创建协程
}
MainScope().launch { //CoroutineScope子类MainScope, 默认运行在主线程中,创建协程
}
使用async创建一个协程:
val deferred = MainScope().async {
}
使用async方法创建协程会返回一个deferred对象,通过deferred.await()挂起函数,可以得到异步获取的值。
下面通过一个简单的例子对将coroutine和rxjava作对比:
使用retrofit+rxjava:
interface ApiService {
@GET("/users.json")
fun getUsers(): Observable
}
class MainAct : AppCompatActivity() {
@Inject lateinit var apiService: ApiService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
private fun getUsersData() {
apiService.getUsers()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe ({
})
}
}
retrofit+coroutine:
@GET("/users.json")
fun getUsers(): Call
///////////
class MainAct : AppCompatActivity(),
CoroutineScope by MainScope() {
@Inject lateinit var apiService: ApiService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
private fun getUsersData() {
launch(Dispatchers.Main) {
val result = apiService.getUsers().await()
}
}
}
我使用kotlin委托的方式, 让MainActivity实现CoroutineScope接口,并委托给MainScope,这样就可以在MainActivity中直接使用launch,而不需要每次都构建CoroutineScope对象了。而retrofit的Call对象,对coronutine有支持,提供了挂起方法await:
suspend fun Call.await(): T {
return suspendCancellableCoroutine { continuation ->
continuation.invokeOnCancellation {
cancel()
}
enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
if (response.isSuccessful) {
val body = response.body()
if (body == null) {
//.....
} else {
continuation.resume(body)
}
} else {
continuation.resumeWithException(HttpException(response))
}
}
override fun onFailure(call: Call, t: Throwable) {
continuation.resumeWithException(t)
}
})
}
}
有时候可能我们需要两个api同时请求,同时获取结果,在RxJava中我们使用zip表达式实现该功能,在retrofit + coroutine中可以直接调用await方法,就可以实现:
launch(Dispatchers.Main) {
val userResult = apiService.getUsers().await()
val user = apiService.getUser("1111").await()
handleData(userResult, user)
}
下一篇会对kotlin coroutine异常处理,以及coroutine部分源码进行分析。