Android 协程使用的心得记录
1.导入
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
2.使用
A. 开启协程的方式:GlobalScope.launch
val launch = GlobalScope.launch {
.....
}
launch接收3个参数,context: CoroutineContext , start: CoroutineStart 以及 block: suspend CoroutineScope.() -> Unit
第三个参数:就是你需要执行的代码,这个不用讲;
第一个参数:(我们可传入以下4种)
Dispatchers.Default
默认线程池,协程体中的代码将被分配到 线程 中执行;Dispatchers.IO
线程池,协程体中的代码将被分配到 线程 中执行;Dispatchers.Main
主线程,协程体中的的代码将在 主线程 中执行;Dispatchers.Unconfined
将在调用协程时所处的线程中执行协程体中的代码;
如:在主线程中调用协程,则协程体中的代码也将运行在主线程中;
第二个参数:
start = CoroutineStart.DEFAULT
作用:立即执行协程体 默认值
println("协程 主线程:${Thread.currentThread()}")
val launch = GlobalScope.launch {
println("协程1:${Thread.currentThread()}")
println("协程1 主线程?:${Looper.getMainLooper() == Looper.myLooper()}")
val deferredA: Deferred<Int> = async(Dispatchers.Default) {
println("协程A:${Thread.currentThread()}")
println("协程A 主线程?:${Looper.getMainLooper() == Looper.myLooper()}")
delay(2000)
var a = 0
for (i in 0..100) a += i
a
}
val deferredB = async(Dispatchers.Default) {
println("协程B:${Thread.currentThread()}")
println("协程B 主线程?:${Looper.getMainLooper() == Looper.myLooper()}")
delay(4000)
var b = 1
for (i in 0..50) b += i
b
}
println("协程:输出第一句")
val i = deferredA.await() + deferredB.await()
println("协程:$i")
println("协程:输出第二句")
}
launch.cancel()//直接取消
打印日志:
I/System.out: 协程 主线程:true
I/System.out: 协程1:Thread[DefaultDispatcher-worker-1,5,main]
I/System.out: 协程1 主线程?:false
I/System.out: 协程:输出第一句
结论:
调用launch.cancel()后,协程体会被直接停止运行;
注释掉launch.cancel()后,打印日志:
I/System.out: 协程 主线程:true
I/System.out: 协程1:Thread[DefaultDispatcher-worker-2,5,main]
I/System.out: 协程1 主线程?:false
I/System.out: 协程A:Thread[DefaultDispatcher-worker-1,5,main]
I/System.out: 协程A 主线程?:false
I/System.out: 协程:输出第一句
I/System.out: 协程B:Thread[DefaultDispatcher-worker-3,5,main]
I/System.out: 协程B 主线程?:false
I/System.out: 协程:6326
I/System.out: 协程:输出第二句
结论:
正常执行,直到协程体执行结束;
start = CoroutineStart.ATOMIC
作用:立即执行协程体,但在开始运行之前无法取消
println("协程 主线程:${Thread.currentThread()}")
val launch = GlobalScope.launch(start = CoroutineStart.ATOMIC) {
println("协程1:${Thread.currentThread()}")
println("协程1 主线程?:${Looper.getMainLooper() == Looper.myLooper()}")
val deferredA: Deferred<Int> = async(Dispatchers.Default) {
println("协程A:${Thread.currentThread()}")
println("协程A 主线程?:${Looper.getMainLooper() == Looper.myLooper()}")
delay(2000)
var a = 0
for (i in 0..100) a += i
a
}
val deferredB = async(Dispatchers.Default) {
println("协程B:${Thread.currentThread()}")
println("协程B 主线程?:${Looper.getMainLooper() == Looper.myLooper()}")
delay(4000)
var b = 1
for (i in 0..50) b += i
b
}
println("协程:输出第一句")
val i = deferredA.await() + deferredB.await()
println("协程:$i")
println("协程:输出第二句")
}
launch.cancel()//直接取消
在调用了 GlobalScope.launch 后,又直接调用取消,打印出来的日志为:
I/System.out: 协程 主线程:true
I/System.out: 协程1:Thread[DefaultDispatcher-worker-1,5,main]
I/System.out: 协程1 主线程?:false
I/System.out: 协程A:Thread[DefaultDispatcher-worker-2,5,main]
I/System.out: 协程A 主线程?:false
I/System.out: 协程:输出第一句
结论:
launch.cancel() 无法停止启动方式为 ATOMIC 的协程,协程体中出现delay()方法时,才会被阻断,并且无法继续执行(协程B无法执行);
start = CoroutineStart.UNDISPATCHED
作用:立即在当前线程执行协程体,直到第一个 suspend 调用;
执行上面相同的代码,修改 start =CoroutineStart.UNDISPATCHED ,删除launch.cancel()
打印日志为:
I/System.out: 协程 主线程:true
I/System.out: 协程1:Thread[main,5,main]
I/System.out: 协程1 主线程?:true
I/System.out: 协程A:Thread[DefaultDispatcher-worker-2,5,main]
I/System.out: 协程A 主线程?:false
I/System.out: 协程B:Thread[DefaultDispatcher-worker-1,5,main]
I/System.out: 协程B 主线程?:false
I/System.out: 协程:输出第一句
I/System.out: 协程:6326
I/System.out: 协程:输出第二句
结论:
协程体将会在当前线程中执行,直到第一个 suspend 调用,async{}的本质也是suspend 修饰的函数,所以在进入async{} 后,切换了线程;
执行上面相同的代码,修改 start =CoroutineStart.UNDISPATCHED ,保留launch.cancel()
打印日志为:
I/System.out: 协程 主线程:true
I/System.out: 协程1:Thread[main,5,main]
I/System.out: 协程1 主线程?:true
I/System.out: 协程A:Thread[DefaultDispatcher-worker-1,5,main]
I/System.out: 协程A 主线程?:false
I/System.out: 协程:输出第一句
I/System.out: 协程B:Thread[DefaultDispatcher-worker-2,5,main]
I/System.out: 协程B 主线程?:false
结论:
launch.cancel() 无法停止启动方式为 UNDISPATCHED 的协程,协程体中出现delay()方法时,才会被阻断,但可继续向下执行 协程B 的代码,直到被在 协程B 中被delay()阻断
start = CoroutineStart.LAZY
作用:不执行协程体的代码,只有在需要的情况下运行(即调用launch.start())
执行上面相同的代码,修改 start =CoroutineStart.LAZY,删除launch.cancel() ,在代码中增加点击事件:
binding.button.setOnClickListener {
launch.start()
}
launch.start() :让协程体开始运行;
点击执行后打印日志:
I/System.out: 协程 主线程:true
I/System.out: 协程1:Thread[DefaultDispatcher-worker-2,5,main]
I/System.out: 协程1 主线程?:false
I/System.out: 协程A:Thread[DefaultDispatcher-worker-1,5,main]
I/System.out: 协程A 主线程?:false
I/System.out: 协程:输出第一句
I/System.out: 协程B:Thread[DefaultDispatcher-worker-3,5,main]
I/System.out: 协程B 主线程?:false
I/System.out: 协程:6326
I/System.out: 协程:输出第二句
结论:
与start = CoroutineStart.DEFAULT执行结果相同,区别在于使用此模式需要自己启动协程;
执行上面相同的代码,修改 点击事件,在运行之后立即取消:
binding.button.setOnClickListener {
launch.start()
launch.cancel()
}
打印日志:
I/System.out: 协程 主线程:true
I/System.out: 协程1:Thread[DefaultDispatcher-worker-1,5,main]
I/System.out: 协程1 主线程?:false
I/System.out: 协程:输出第一句
结论:
与start = CoroutineStart.DEFAULT执行结果相同,会立即取消协程体的执行;
B. suspendCoroutine<T>
作用:
挂起函数,可以封装获取数据的过程;
返回T中的数据类型,通过调用 it.resume(T) 返回
也可返回异常,通过调用 it.resumeWithException(NullPointerException()) 返回
- 写出一个数据类
data class TestItem(var name: String, val id: Int)
- 模拟请求获取到数据
private suspend fun getTestCoroutine() = suspendCoroutine<TestItem> {
val testItem = TestItem("coroutine", 1)
it.resume(testItem)
}
- 发起请求
val launch = GlobalScope.launch(Dispatchers.Main) {
val async = async(Dispatchers.Default) { getTestCoroutine().name }
println(async.await())
}
打印日志:
I/System.out: coroutine
- 修改模拟请求,模拟其请求异常并抛出
private suspend fun getTestCoroutine() = suspendCoroutine<TestItem> {
val testItem = TestItem("coroutine", 1)
it.resumeWithException(NullPointerException())
}
- 再修改发起请求的代码,接收异常:
val launch = GlobalScope.launch(context = Dispatchers.Main) {
val async = async(Dispatchers.Default) {
try {
getTestCoroutine().name
} catch (e: Exception) {
println("接收到异常")
}
}
println(async.await())
}
打印出的日志:
I/System.out: 接收到异常
I/System.out: kotlin.Unit
C.异常处理方案
- 定义CoroutineExceptionHandler
val exceptionHandler =
CoroutineExceptionHandler { coroutineContext, throwable -> println("接收到异常") }
1.修改发起请求的代码:
val launch = GlobalScope.launch(context = Dispatchers.Main + exceptionHandler) {
val async = async(Dispatchers.Default) {
getTestCoroutine().name
}
println(async.await())
}
2.这样子,我们就能在 exceptionHandler 当中统一接收异常并处理;
3.context = Dispatchers.Main + exceptionHandler 的写法是因为内部重写了plus()方法,所以我们直接用+号即可同时拥有俩种属性;
4.打印日志:
I/System.out: 接收到异常
5.如果我们将接收异常的exceptionHandler 放入async中,如下:
val launch = GlobalScope.launch(context = Dispatchers.Main) {
val async = async(context = exceptionHandler + Dispatchers.Default) {
getTestCoroutine().name
}
println(async.await())
}
则,程序会直接崩溃,因为在async中,会忽略CoroutineExceptionHandler,并传播异常出去,因为其本质为 coroutineScope,关于这点,将在下面详细写出;
D.supervisorScope 与 coroutineScope
supervisorScope 特点:任何一个任务出问题,并不会影响其他任务的工作coroutineScope 特点:任何一个子协程异常退出,那么整体都将退出
- 定义三个函数
private fun getA(): String {
println("正常a")
return "正常a"
}
private fun getB(): String {
println("正常b")
return "正常b"
}
private fun getE(): String {
throw NullPointerException()
}
val exceptionHandler =
CoroutineExceptionHandler { coroutineContext, throwable -> println("接收到异常") }
- 使用
supervisorScope
val launch = GlobalScope.launch(context = Dispatchers.Main) {
supervisorScope {
println("supervisorScope:" + Thread.currentThread())
launch {
println("launchA:" + Thread.currentThread())
getA()
}
launch(exceptionHandler) {
println("launchE:" + Thread.currentThread())
getE()
}
val lau = launch {
delay(1000)
println("launchB:" + Thread.currentThread())
getB()
}
lau.join()
}
}
在getE()处的launch我们用exceptionHandler接收了异常,那么打印出的日志为:
I/System.out: supervisorScope:Thread[main,5,main]
I/System.out: launchA:Thread[main,5,main]
I/System.out: 正常a
I/System.out: launchE:Thread[main,5,main]
I/System.out: 接收到异常
I/System.out: launchB:Thread[main,5,main]
I/System.out: 正常b
我们可以看到,launchB被正常执行;并不被launchE所影响;
我们再修改一下代码,将接收异常移动到最外层,观察会发生什么:
val launch = GlobalScope.launch(context = Dispatchers.Main) {
try {
supervisorScope {
println("supervisorScope:" + Thread.currentThread())
launch {
println("launchA:" + Thread.currentThread())
getA()
}
launch {
println("launchE:" + Thread.currentThread())
getE()
}
val launch = launch {
delay(1000)
println("launchB:" + Thread.currentThread())
getB()
}
launch.join()
}
} catch (e: Exception) {
println("异常")
}
}
打印日志:
I/System.out: supervisorScope:Thread[main,5,main]
I/System.out: launchA:Thread[main,5,main]
I/System.out: 正常a
I/System.out: launchE:Thread[main,5,main]
--------- beginning of crash
E/AndroidRuntime: FATAL EXCEPTION: main
可以看到,程序执行到 launchE 时,直接崩溃,我们可以得知,supervisorScope 并不会接收到内部的子协程异常,而是必须子协程自己处理,这样便不会影响到其他子线程的执行,继续往下看,将更理解这段话;
- 使用
coroutineScope
修改代码:
val launch = GlobalScope.launch(context = Dispatchers.Main) {
coroutineScope {
println("coroutineScope:" + Thread.currentThread())
launch {
println("launchA:" + Thread.currentThread())
getA()
}
launch(exceptionHandler) {
println("launchE:" + Thread.currentThread())
getE()
}
val launch = launch {
delay(1000)
println("launchB:" + Thread.currentThread())
getB()
}
launch.join()
}
I/System.out: coroutineScope:Thread[main,5,main]
I/System.out: launchA:Thread[main,5,main]
I/System.out: 正常a
I/System.out: launchE:Thread[main,5,main]
--------- beginning of crash
E/AndroidRuntime: FATAL EXCEPTION: main
我们可以看到,程序直接崩溃了,因为launchE 的崩溃,并没有办法被独立处理,而是会导致整个协程直接退出;
我们再修改代码,将接收异常移动到最外层,我们将更清楚的了解coroutineScope 的特性:
val launch = GlobalScope.launch(context = Dispatchers.Main) {
try {
coroutineScope {
println("coroutineScope:" + Thread.currentThread())
launch {
println("launchA:" + Thread.currentThread())
getA()
}
launch(exceptionHandler) {
println("launchE:" + Thread.currentThread())
getE()
}
val launch = launch {
delay(1000)
println("launchB:" + Thread.currentThread())
getB()
}
launch.join()
}
} catch (e: Exception) {
println("异常")
}
}
打印出日志:
I/System.out: coroutineScope:Thread[main,5,main]
I/System.out: launchA:Thread[main,5,main]
I/System.out: 正常a
I/System.out: launchE:Thread[main,5,main]
I/System.out: 异常
我们可以看到,异常已经被接收,并且不会崩溃,但是 launchB 并没有被执行,launchE 的异常影响了其他协程的正常执行,可以得出:coroutineScope任何一个子协程异常退出,那么整体都将退出
更多推荐

所有评论(0)