Kotlin Coroutine初探(一)
2017年,Google 宣布Kotlin
成为 Android 的官方开发语言,并在1.1版本加入了对 Coroutine(协程,可以简单看作是轻量级线程)的支持。
Coroutine
是一种并发设计模式,在 Android 平台上使用它可以简化异步执行的代码。下面就让我们一起使用Coroutine
进行编码吧!
启动协程
协程任务的构建需要通过Coroutine Builder
来实现。
/**
* @param context 默认所创建的 Coroutine 会自动继承当前 Coroutine 的 context,如果有额外的 context 需要传递给所创建的 Coroutine 则可以通过第一个参数来设置
* @param start 配置协程的启动模式,包含 DEFAULT、LAZY、ATOMIC和UNDISPATCHED四种模式
* @param block 需要执行的方法
*
* @return 返回生成的协程任务对象Job
* */
fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job
默认的Dispatchers
构建协程时,使用Dispatchers
中定义的类型来实现。
fun testDispatchers() {
try {
val defaultJob = GlobalScope.launch(context = Dispatchers.Default) { // 在后台启动一个新的协程并继续
Log.e("ViewModel", "start--defaultJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
withContext(context = Dispatchers.IO) {
Log.e("ViewModel", "launch--defaultJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
}
Log.e("ViewModel", "after--defaultJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
}
val mainJob = GlobalScope.launch(context = Dispatchers.Main) { // 在后台启动一个新的协程并继续
Log.e("ViewModel", "start--mainJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
withContext(context = Dispatchers.IO) {
Log.e("ViewModel", "launch--mainJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
}
Log.e("ViewModel", "after--mainJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
}
val unconfinedJob = GlobalScope.launch(context = Dispatchers.Unconfined) { // 在后台启动一个新的协程并继续
Log.e("ViewModel", "start--unconfinedJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
withContext(context = Dispatchers.IO) {
Log.e("ViewModel", "launch--unconfinedJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
}
Log.e("ViewModel", "after--unconfinedJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
}
val ioJob = GlobalScope.launch(context = Dispatchers.IO) { // 在后台启动一个新的协程并继续
Log.e("ViewModel", "start--ioJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
withContext(context = Dispatchers.IO) {
Log.e("ViewModel", "launch--ioJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
}
Log.e("ViewModel", "after--ioJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
}
} catch (e: Exception) {
println(e.localizedMessage)
}
}
输出:
- Dispatchers.Default,执行任务时,使用默认的协程线程池,线程个数为
CPU个数
但不能少于2个。
- Dispatchers.Main,执行任务时,使用Android默认的UI线程。
- Dispatchers.Unconfined,执行任务时,使用当前线程。
- Dispatchers.IO,执行任务时,使用默认的IO线程池,线程个数为
Math.max(64,CPU个数)
。
默认的CoroutineStart
构建协程时,使用CoroutineStart
中定义的类型来实现。
fun testCoroutineStart() {
try {
val defaultJob = GlobalScope.launch(start = CoroutineStart.DEFAULT) { // 在后台启动一个新的协程并继续
Log.e("ViewModel", "start--defaultJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
withContext(context = Dispatchers.IO) {
Log.e("ViewModel", "launch--defaultJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
}
Log.e("ViewModel", "after--defaultJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
}
val lazyJob = GlobalScope.launch(start = CoroutineStart.LAZY) { // 在后台启动一个新的协程并继续
Log.e("ViewModel", "start--lazyJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
withContext(context = Dispatchers.IO) {
Log.e("ViewModel", "launch--lazyJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
}
Log.e("ViewModel", "after--lazyJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
}
lazyJob.start()
val atomicJob = GlobalScope.launch(start = CoroutineStart.ATOMIC) { // 在后台启动一个新的协程并继续
Log.e("ViewModel", "start--atomicJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
withContext(context = Dispatchers.IO) {
Log.e("ViewModel", "launch--atomicJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
}
Log.e("ViewModel", "after--atomicJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
}
atomicJob.cancel()
val undispatchedJob = GlobalScope.launch(start = CoroutineStart.UNDISPATCHED) { // 在后台启动一个新的协程并继续
Log.e("ViewModel", "start--undispatchedJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
withContext(context = Dispatchers.IO) {
Log.e("ViewModel", "launch--undispatchedJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
}
Log.e("ViewModel", "after--undispatchedJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
}
} catch (e: Exception) {
println(e.localizedMessage)
}
}
输出:
- CoroutineStart.DEFAULT,默认的启动模式,会直接开始执行任务,不需要单独调用
job.start()
。
- CoroutineStart.LAZY,懒加载模式,只有在调用了
job.start()
才会开始执行协程任务。
- CoroutineStart.ATOMIC,原子模式,会直接开始执行任务,但取消任务时,如果任务还没开始执行,会取消不了任务。
- CoroutineStart.UNDISPATCHED,不调度模式,会直接开始执行任务,但切换协程被挂起(切换线程)之后,再次resume时不会切换到之前启动的线程,而是以当前执行的线程去继续执行任务 。
取消协程
协程构建之后,会返回Job
对象,可以用来取消协程。
Job.cancel(cause: Throwable? = null): Boolean
抛出指定的异常来取消协程,如果传递空则抛出CancellationException
。
fun testCancelCoroutine() {
try {
Log.e("ViewModel", "testCancelCoroutine")
val defaultCancelJob = GlobalScope.launch { // 在后台启动一个新的协程并继续
Log.e("ViewModel", "start--defaultCancelJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
withContext(context = Dispatchers.IO) {
delay(10000)//延时10s
Log.e("ViewModel", "launch--defaultCancelJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
}
Log.e("ViewModel", "after--defaultCancelJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
}
defaultCancelJob.cancel()
} catch (e: Exception) {
e.printStackTrace()
}
}
输出:
Job.cancelAndJoin()
等到协程任务被执行完成,才会取消。
fun testCancelAndJoinCoroutine() {
try {
Log.e("ViewModel", "testCancelAndJoinCoroutine")
val defaultCancelAndJoinJob = GlobalScope.launch { // 在后台启动一个新的协程并继续
Log.e("ViewModel", "start--defaultCancelAndJoinJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
withContext(context = Dispatchers.IO) {
delay(10000)//延时10s
Log.e("ViewModel", "launch--defaultCancelAndJoinJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
}
Log.e("ViewModel", "after--defaultCancelAndJoinJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
}
launch {
defaultCancelAndJoinJob.cancelAndJoin()
Log.e("ViewModel", "cancelAndJoin")
}
} catch (e: Exception) {
e.printStackTrace()
}
}
输出: