Kotlin协程之launch

协程之launch

协程需要依赖以下包

dependencies {
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2'
}

launch

launch必须放在协程里才行,launch开启的协程不会阻塞上下文线程,如下:

import kotlinx.coroutines.*
fun main() {
    GlobalScope.launch { // 在后台启动一个新的协程并继续
        println("${Thread.currentThread().name}")
        delay(1000L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
        println("World!") // 在延迟后打印输出
    }
    println("${Thread.currentThread().name}")
    println("Hello,") // 协程已在等待时主线程还在继续
    Thread.sleep(2000L) // 阻塞主线程 2 秒钟来保证 JVM 存活
}

结果:

main
Hello,
DefaultDispatcher-worker-1 @coroutine#1
World!

GlobalScope.launch

GlobalScope.launch可以在代码的任何地方运行,它会在新的工作线程上执行,如上述例子,新的线程名称为:DefaultDispatcher-worker-1 @coroutine#1 。GlobalScope.launch会创建一个全局的线程池,所以多个GlobalScope.launch调用不一定会创建多个线程,如果有空闲线程,则会复用空闲线程。如下所示例子:

      GlobalScope.launch {
          GlobalScope.launch {
               for (i in 1..10){
                   delay(1000)
                   println("${Thread.currentThread().name} 2")
               }
          }
          for (i in 1..10){
               forTest(1000)
          }
     }
     println("${Thread.currentThread().name} 4")
            
    private suspend fun forTest(time:Long){
        delay(1000)
        println("${Thread.currentThread().name} 3")
    }

结果

2021-06-12 18:20:34.757 22096-22096/? I/System.out: main 4
2021-06-12 18:20:35.767 22096-22759/? I/System.out: DefaultDispatcher-worker-1 2
2021-06-12 18:20:35.767 22096-22760/? I/System.out: DefaultDispatcher-worker-2 3
2021-06-12 18:20:36.770 22096-22760/? I/System.out: DefaultDispatcher-worker-2 2
2021-06-12 18:20:36.771 22096-22760/? I/System.out: DefaultDispatcher-worker-2 3
2021-06-12 18:20:37.773 22096-22759/? I/System.out: DefaultDispatcher-worker-1 2
2021-06-12 18:20:37.776 22096-22759/? I/System.out: DefaultDispatcher-worker-1 3
2021-06-12 18:20:38.778 22096-22760/? I/System.out: DefaultDispatcher-worker-2 2
2021-06-12 18:20:38.779 22096-22760/? I/System.out: DefaultDispatcher-worker-2 3
2021-06-12 18:20:39.782 22096-22759/? I/System.out: DefaultDispatcher-worker-1 2
2021-06-12 18:20:39.783 22096-22759/? I/System.out: DefaultDispatcher-worker-1 3
2021-06-12 18:20:40.786 22096-22760/? I/System.out: DefaultDispatcher-worker-2 2
2021-06-12 18:20:40.788 22096-22760/? I/System.out: DefaultDispatcher-worker-2 3
2021-06-12 18:20:41.789 22096-22760/? I/System.out: DefaultDispatcher-worker-2 2
2021-06-12 18:20:41.791 22096-22759/? I/System.out: DefaultDispatcher-worker-1 3
2021-06-12 18:20:42.793 22096-22760/? I/System.out: DefaultDispatcher-worker-2 2
2021-06-12 18:20:42.795 22096-22760/? I/System.out: DefaultDispatcher-worker-2 3
2021-06-12 18:20:43.796 22096-22759/? I/System.out: DefaultDispatcher-worker-1 2
2021-06-12 18:20:43.798 22096-22759/? I/System.out: DefaultDispatcher-worker-1 3
2021-06-12 18:20:44.798 22096-22760/? I/System.out: DefaultDispatcher-worker-2 2
2021-06-12 18:20:44.799 22096-22760/? I/System.out: DefaultDispatcher-worker-2 3

可以看出,DefaultDispatcher-worker-1DefaultDispatcher-worker-2有时候会交替执行两个不同协程里面的内容。

那么,如果在GlobalScope.launch中直接调用launch呢?此时内部launch是属于谁的launch?答案是,属于GlobalScopelaunch,示例如下:

      GlobalScope.launch {
          launch {
               for (i in 1..10){
                   delay(1000)
                   println("${Thread.currentThread().name} 2")
               }
          }
          for (i in 1..10){
               forTest(1000)
          }
     }
     println("${Thread.currentThread().name} 4")
            
    private suspend fun forTest(time:Long){
        delay(1000)
        println("${Thread.currentThread().name} 3")
    }

结果与上述结果一样,DefaultDispatcher-worker-1DefaultDispatcher-worker-2有时候会交替执行两个不同协程里面的内容。其实可以通过launch的源码看出来,launch是属于调用它的scope,且内层的launch跟外层的launch共用同一个scope类型

public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
        LazyStandaloneCoroutine(newContext, block) else
        StandaloneCoroutine(newContext, active = true)
    coroutine.start(start, coroutine, block)
    return coroutine
}

viewModelScope.launch

viewModelScope.launch必须在ViewModel中执行,否则会报错。默认是在主线程上执行,如下所示例子:

class MainViewModel: ViewModel() {
    fun testViewModel(){
        println("${Thread.currentThread().name} 1")
        viewModelScope.launch {
            println("${Thread.currentThread().name} 2")
            forTest()
        }
        println("${Thread.currentThread().name} 4")
    }
    private suspend fun forTest(){
        delay(100)
        println("${Thread.currentThread().name} 3")
    }
}

结果

2021-06-12 17:35:24.101 4370-4370/? I/System.out: main 1
2021-06-12 17:35:24.130 4370-4370/? I/System.out: main 2
2021-06-12 17:35:24.132 4370-4370/? I/System.out: main 4
2021-06-12 17:35:24.233 4370-4370/? I/System.out: main 3

可以看出,launch不会阻塞当前的线程,println4println3之前执行
viewModelScope.launch不管其上下文是否是主线程,未指定Dispathers情况下,默认是在主线程中执行,如下例子:

  fun testViewModel(){
        println("${Thread.currentThread().name} 1")
        viewModelScope.launch(Dispatchers.Main) {
            withContext(Dispatchers.IO){
                viewModelScope.launch {
                    delay(1000)
                    println("${Thread.currentThread().name} 2")
                }
                println("${Thread.currentThread().name} 3")
            }
            println("${Thread.currentThread().name} 4")
            forTest()
        }
        println("${Thread.currentThread().name} 6")
    }
    private suspend fun forTest(){
        delay(1000)
        println("${Thread.currentThread().name} 5")
    }

结果

2021-06-12 18:03:52.768 20168-20168/? I/System.out: main 1
2021-06-12 18:03:52.803 20168-20168/? I/System.out: main 6
2021-06-12 18:03:52.825 20168-20976/? I/System.out: DefaultDispatcher-worker-1 3
2021-06-12 18:03:52.827 20168-20168/? I/System.out: main 4
2021-06-12 18:03:53.830 20168-20168/? I/System.out: main 2
2021-06-12 18:03:53.833 20168-20168/? I/System.out: main 5

可以看到,第二个默认的viewModelScope.launch,其上下文线程是DefaultDispatcher-worker-1,自身协程是在主线程中运行的。

Dispathers

Dispatchers的调度器分别有Default, Main, Unconfined,IOlaunch可以指定协程运行所在线程,如下:

    btn.setOnClickListener {
        GlobalScope.launch(Dispatchers.Main) {
            println("${Thread.currentThread().name} 1")
            forTest()
        }
        println("${Thread.currentThread().name} 3")
    }
        
    private suspend fun forTest(){
        delay(1000)
        println("${Thread.currentThread().name} 2")
    }

结果

2021-06-12 17:51:56.733 19809-19809/? I/System.out: main 3
2021-06-12 17:51:56.738 19809-19809/? I/System.out: main 1
2021-06-12 17:51:57.744 19809-19809/? I/System.out: main 2

我们知道,GlobalScope.launch默认情况下是会单独开启一个工作线程来执行协程里的内容,在这里,我们通过Dispatchers.Main指定了GlobalScope.launch协程在主线程上执行,打印结果确实是在主线程上。

总结

  1. viewModelScope.launch必须在viewModel中执行,且未指定运行线程情况下,默认是在主线程中执行,且不会阻塞当前上下文线程
  2. GlobalScope.launch可以在任何地方执行,在未指定运行线程的情况下,默认会开启新的工作线程来执行协程,且不会阻塞当前上下文线程,它底层是一个全局的线程池,多个GlobalScope.launch可能会使用同一个线程来执行协程。
  3. launch必须在协程中
  4. launch可以通过Dispathers来指定协程所运行的线程
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值