目录
协程可以简单地理解成一种轻量级的线程。
kotlin
fun foo(){
a()
b()
c()
}
fun bar(){
x()
y()
z()
}
在协程A中去调用foo()方法,协程B中去调用bar()方法,他们会运行在同一个线程当中,在执行foo()方法时都有可能被挂起转而执行bar()方法,执行bar()方法时也随时都有可能被挂起转而执行foo()方法。
协程允许我们在单线程下模拟多线程编程的效果。
协程的基本用法
启动一个协程
kotlin
fun main() {
GlobalScope.launch {
println("codes run in coroutine scope")
}
}
运行后发现没有打印出来,原因是还没等打印出来,程序就结束了。
kotlin
fun main() {
GlobalScope.launch {
println("codes run in coroutine scope")
}
Thread.sleep(1000)
}
执行结果
kotlin
codes run in coroutine scope
Process finished with exit code 0
如果程序结束,协程没执行完也会结束。可以使用runBlocking,在协程中所有代码都运行完了之后再结束。
kotlin
fun main() {
runBlocking {
println("codes run in coroutine scope")
delay(1500)
println("end")
}
}
kotlin
codes run in coroutine scope
end
Process finished with exit code 0
创建多个协程
kotlin
runBlocking {
launch {
println("codes run in coroutine scope")
delay(1500)
println("end")
}
launch {
println("111111111111111")
delay(1500)
println("end2")
}
}
代码中launch 必须在协程中才能调用,它会在当前协程的作用域下创建子协程。子协程的特点是如果外层作用域的协程结束了,该作用域下的所有子协程也会一同结束。
逻辑越来越复杂,可以将部分代码抽离到一个函数中。如何在抽离出的函数中使用像delay()这种挂起函数,可以使用suspend关键字,可以将任意函数声明成挂起函数。
kotlin
suspend fun myCall(){
println("hi ")
delay(1000)
}
suspend 无法提供协程作用域,不能调用launch函数。使用coroutineScope函数可以处理此问题。coroutineScope是一个挂起函数。
kotlin
suspend fun myCall(){
coroutineScope {
launch {
println("hi ")
delay(1000)
}
}
}
coroutineScope会将外部协程挂起,只有当它作用域内的所有代码和子协程都执行完毕后,coroutineScope函数之后的代码才能得到运行。
coroutineScope函数只会阻塞当前协程,不会造成性能上的问题。runBlocking 会挂起外部线程,如果在主线程中当中调用它的话,可能会导致界面卡死,一般不推荐使用。
作用域构建器
取消协程
kotlin
val job = GlobalScope.launch {
}
job.cancel()
比较常用的写法
kotlin
val job = Job()
val scope = CoroutineScope(job)
scope.launch {
}
job.cancel()
CoroutineScope.launch 所创建的协程,都会被关联在job对象的作用域下面,调用一次cancel方法,可以将同一作用域的所有协程全部取消。
有执行结果的协程,要使用async函数,会创建一个新的子协程并返回一个Deferred对象,获取执行结果需要调用Deferred对象的await()方法。
kotlin
runBlocking {
val result = async {
5 + 5
}.await()
println(result)
}
await()如果代码块中的代码还没执行完,那么await()方法会将当前协程阻塞,直到获取到async 的结果。
withContext()
调用withContext()函数后,会立即执行代码块中的代码,同时将外部协程挂起,当代码块中的代码执行完成后,会将最后一行代码作为返回值返回。withContext()函数强制要求指定一个线程参数。
kotlin
runBlocking {
val result = withContext(Dispatchers.Default) {
5 + 5
}
println(result)
}
Dispatchers有三种值,Dispatchers.Default、Dispatchers.Main、Dispatchers.IO,Dispatchers.Default表示会使用一种默认低并发的线程策略,Dispatchers.IO表示会使用一种较高并发的线程策略,当你要执行的代码大多数时间是在阻塞和等待中,比如说执行网络请求时。Dispatchers.Main则表示在android主线程执行。
使用协程简化回调的写法
suspendCoroutine函数必须在协程作用域或挂起函数中才能调用,主要作用是将当前协程立即挂起,在一个普通的线程中执行Lambda表达式中的代码。Lambda表达式的参数列表上会传入一个Continuation参数,调用它的resume()或resumeWithException()来让协程恢复。
简化Retrofit的网络请求
kotlin
val appService = ServiceCreator.create<AppService>()
appService.getData().enqueue(object : Callback<WordListBean>{
override fun onResponse(call: Call<WordListBean?>, response: Response<WordListBean?>) {
Log.d("ClickActivity",response.body().toString())
}
override fun onFailure(call: Call<WordListBean?>, t: Throwable) {
Log.d("ClickActivity",t.toString())
}
})
使用suspendCoroutine
kotlin
suspend fun <T> Call<T>.await():T{
return suspendCoroutine { continuation ->
enqueue(object : Callback<T>{
override fun onResponse(call: Call<T?>, response: Response<T?>) {
val body = response.body()
if(body !=null){
continuation.resume(body)
}else{
continuation.resumeWithException(RuntimeException("body is null"))
}
}
override fun onFailure(call: Call<T?>, t: Throwable) {
continuation.resumeWithException(t)
}
})
}
}
kotlin
suspend fun getAppData() {
try {
val appList = ServiceCreator.create<AppService>().getData().await()
} catch (e: Exception) {
}
}