学习总结
协程
GlobalScope.launch 每次创建顶层协程,且不会阻塞外部线程;在实际项目中不太常用
GlobalScope.launch {
println("codes run in coroutine scope")
}
runBlocking 函数同样会创建一个协程的作用域,但是它可以保证在协程作用域内的所有代码和子协程没有全部执行完之前一直阻塞当前线程;runBlocking函数通常只应该在测试环境下使用,在正式环境中使用容易产生一些性能上的问题
runBlocking {
println("codes run in coroutine scope")
delay(1500) // 协程特有的,类似java中的Thread.sleep(1500)
println("codes run in coroutine scope finished")
}
launch 首先它必须在协程的作用域中才能调用,其次它会在当前协程的作用域下创建子协程。子协程的特点是如果外层作用域的协程结束了,该作用域下的所有子协程也会一同结束
runBlocking { // 必须先给他创造个协程作用域,测试环境用runBlocking比较方便
launch {
println("launch1")
delay(1000)
println("launch1 finished")
}
}
coroutineScope coroutineScope函数也是一个挂起函数,因此可以在任何其他挂起函数中调用。它的特点是会继承外部的协程的作用域并创建一个子协程,借助这个特性,我们就可以给任意挂起函数提供协程作用域了。coroutineScope函数和runBlocking函数还有点类似,它可以保证其作用域内的所有代码和子协程在全部执行完之前,外部的协程会一直被挂起;但是coroutineScope函数只会阻塞当前协程,既不影响其他协程,也不影响任何线程,因此是不会造成任何性能上的问题的
suspend fun printDot() = coroutineScope {
launch {
println(".")
delay(1000)
}
}
前面几个方式的返回结果都是Job,无法获取协程执行结果
suspend 它可以将任意函数声明成挂起函数,是无法给它提供协程作用域的
suspend fun printDot() {
println(".")
delay(1000)
}
书中的常用创建协程的方式
val job = Job()
val scope = CoroutineScope(job) // 可以用这种方式创建n个协程,并且关联同一个job
scope.launch {
// 处理具体的逻辑
}
job.cancel() // 取消所有job关联的协程
async async函数必须在协程作用域当中才能调用,它会创建一个新的子协程并返回一个Deferred
对象,如果我们想要获取async函数代码块的执行结果,只需要调用Deferred对象的await()方法即可
fun main() {
runBlocking {
val result = async {
5 + 5
}.await()
println(result) // 输出:10
}
}
在调用了async函数之后,代码块中的代码就会立刻开始执行。当调用await()方法时,如果代码块中的代码还没执行完,那么await()方法会将当前协程阻塞住,直到可以获得async函数的执行结果
runBlocking {
val start = System.currentTimeMillis()
val result1 = async {
delay(1000)
5 + 5
}.await()
val result2 = async {
delay(1000)
4 + 6
}.await()
println("result is ${result1 + result2}.")
val end = System.currentTimeMillis()
println("cost ${end - start} ms.") // 输出:cost 2032ms ,说明两个async函数是串行
}
这种写法明显是非常低效的,因为两个async函数完全可以同时执行从而提高运行效率。现在对上述代码使用如下的写法进行修改
runBlocking {
val start = System.currentTimeMillis()
val deferred1 = async {
delay(1000)
5 + 5
}
val deferred2 = async {
delay(1000)
4 + 6
}
println("result is ${deferred1.await() + deferred2.await()}.") // 在这之前都是并行
val end = System.currentTimeMillis()
println("cost ${end - start} milliseconds.") // 输出:cost 1029 milliseconds
}
withContext withContext()函数是一个挂起函数,大体可以将它理解成async函数的一种简化版写法
runBlocking {
val result = withContext(Dispatchers.Default) {
5 + 5
}
println(result) // 输出:10
}
suspendCoroutine 必须在协程作用域或挂起函数中才能调用,它接收一个Lambda表达式参数,主要作用是将当前协程立即挂起,然后在一个普通的线程中执行Lambda表达式中的代码。Lambda表达式的参数列表上会传入一个Continuation参数,调用它的resume()方法或resumeWithException()可以让协程恢复执行
suspend fun request(address: String): String {
return suspendCoroutine { continuation ->
HttpUtil.sendHttpRequest(address, object : HttpCallbackListener {
override fun onFinish(response: String) {
continuation.resume(response) // 返回正常结果
}
override fun onError(e: Exception) {
continuation.resumeWithException(e) // 返回异常
}
})
}
}