欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Kotlin關(guān)于協(xié)程是什么的探究

 更新時(shí)間:2023年01月12日 15:50:12   作者:麥田里的守望者-Jiang  
Android官方對(duì)協(xié)程的定義-協(xié)程是一種并發(fā)設(shè)計(jì)模式,您可以在Android平臺(tái)上使用它來(lái)簡(jiǎn)化異步執(zhí)行的代碼。協(xié)程是在版本1.3中添加到Kotlin的,它基于來(lái)自其他語(yǔ)言的既定概念

Kotlin協(xié)程是什么

本文只是自己經(jīng)過(guò)研究后,對(duì) Kotlin 協(xié)程的理解概括,如有偏差,還請(qǐng)斧正。

簡(jiǎn)要概括:

協(xié)程是 Kotlin 提供的一套線程 API 框架,可以很方便的做線程切換。 而且在不用關(guān)心線程調(diào)度的情況下,能輕松的做并發(fā)編程。也可以說(shuō)協(xié)程就是一種并發(fā)設(shè)計(jì)模式。

下面是使用傳統(tǒng)線程和協(xié)程執(zhí)行任務(wù):

       Thread{
            //執(zhí)行耗時(shí)任務(wù)
        }.start()
        val executors = Executors.newCachedThreadPool()
        executors.execute {
          //執(zhí)行耗時(shí)任務(wù)
        }
       GlobalScope.launch(Dispatchers.IO) {
          //執(zhí)行耗時(shí)任務(wù)
        }

在實(shí)際應(yīng)用開(kāi)發(fā)中,通常是在主線中去啟動(dòng)子線程執(zhí)行耗時(shí)任務(wù),等耗時(shí)任務(wù)執(zhí)行完成,再將結(jié)果給主線程,然后刷新UI:

       Thread{
            //執(zhí)行耗時(shí)任務(wù)
            runOnMainThread { 
                //獲取耗時(shí)任務(wù)結(jié)果,刷新UI
            }
        }.start()
        val executors = Executors.newCachedThreadPool()
        executors.execute {
            //執(zhí)行耗時(shí)任務(wù)
            runOnMainThread {
                //獲取耗時(shí)任務(wù)結(jié)果,刷新UI
            }
        }
        Observable.unsafeCreate<Unit> {
            //執(zhí)行耗時(shí)任務(wù)
        }.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe {
            //獲取耗時(shí)任務(wù)結(jié)果,刷新UI
        }
        GlobalScope.launch(Dispatchers.Main) {
            val result = withContext(Dispatchers.IO){
                //執(zhí)行耗時(shí)任務(wù)
            }
            //直接拿到耗時(shí)任務(wù)結(jié)果,刷新UI
            refreshUI(result)
        }

從上面可以看到,使用Java 的 ThreadExecutors 都需要手動(dòng)去處理線程切換,這樣的代碼不僅不優(yōu)雅,而且有一個(gè)重要問(wèn)題,那就是要去處理與生命周期相關(guān)的上下文判斷,這導(dǎo)致邏輯變復(fù)雜,而且容易出錯(cuò)。

RxJava 是一套優(yōu)雅的異步處理框架,代碼邏輯簡(jiǎn)化,可讀性和可維護(hù)性都很高,很好的幫我們處理線程切換操作。這在 Java 語(yǔ)言環(huán)境開(kāi)發(fā)下,是如虎添翼,但是在 Kotlin 語(yǔ)言環(huán)境中開(kāi)發(fā),如今的協(xié)程就比 RxJava 更方便,或者說(shuō)更有優(yōu)勢(shì)。

下面看一個(gè) Kotlin 中使用協(xié)程的例子:

        GlobalScope.launch(Dispatchers.Main) {
            Log.d("TestCoroutine", "launch start: ${Thread.currentThread()}")
            val numbersTo50Sum = withContext(Dispatchers.IO) {
                //在子線程中執(zhí)行 1-50 的自然數(shù)和
                Log.d("TestCoroutine", "launch:numbersTo50Sum: ${Thread.currentThread()}")
                delay(1000)
                val naturalNumbers = generateSequence(0) { it + 1 }
                val numbersTo50 = naturalNumbers.takeWhile { it <= 50 }
                numbersTo50.sum()
            }
            val numbers50To100Sum = withContext(Dispatchers.IO) {
               //在子線程中執(zhí)行 51-100 的自然數(shù)和
                Log.d("TestCoroutine", "launch:numbers50To100Sum: ${Thread.currentThread()}")
                delay(1000)
                val naturalNumbers = generateSequence(51) { it + 1 }
                val numbers50To100 = naturalNumbers.takeWhile { it in 51..100 }
                numbers50To100.sum()
            }
            val result = numbersTo50Sum + numbers50To100Sum
            Log.d("TestCoroutine", "launch end:result=$result ${Thread.currentThread()}")
        }
        Log.d("TestCoroutine", "Hello World!,${Thread.currentThread()}")

控制臺(tái)輸出結(jié)果:
2023-01-02 16:05:45.846 10153-10153/com.wangjiang.example D/TestCoroutine: Hello World!,Thread[main,5,main]
2023-01-02 16:05:48.058 10153-10153/com.wangjiang.example D/TestCoroutine: launch start: Thread[main,5,main]
2023-01-02 16:05:48.059 10153-10322/com.wangjiang.example D/TestCoroutine: launch:numbersTo50Sum: Thread[DefaultDispatcher-worker-1,5,main]
2023-01-02 16:05:49.114 10153-10322/com.wangjiang.example D/TestCoroutine: launch:numbers50To100Sum: Thread[DefaultDispatcher-worker-1,5,main]
2023-01-02 16:05:50.376 10153-10153/com.wangjiang.example D/TestCoroutine: launch end:result=5050 Thread[main,5,main]

在上面的代碼中:

  • launch 是一個(gè)函數(shù),用于創(chuàng)建協(xié)程并將其函數(shù)主體的執(zhí)行分派給相應(yīng)的調(diào)度程序。
  • Dispatchers.MAIN 指示此協(xié)程應(yīng)在為 UI 操作預(yù)留的主線程上執(zhí)行。
  • Dispatchers.IO 指示此協(xié)程應(yīng)在為 I/O 操作預(yù)留的線程上執(zhí)行。
  • withContext(Dispatchers.IO) 將協(xié)程的執(zhí)行操作移至一個(gè) I/O 線程。

從控制臺(tái)輸出結(jié)果中,可以看出在計(jì)算 1-50 和 51-100 的自然數(shù)和的時(shí)候,線程是從主線程(Thread[main,5,main])切換到了協(xié)程的線程(DefaultDispatcher-worker-1,5,main),這里計(jì)算 1-50 和 51-100 都是同一個(gè)子線程。

在這里有一個(gè)重要的現(xiàn)象,代碼從邏輯上看起來(lái)是同步的,并且啟動(dòng)協(xié)程執(zhí)行任務(wù)的時(shí)候,沒(méi)有阻塞主線程繼續(xù)執(zhí)行相關(guān)操作,而且在協(xié)程中的異步任務(wù)執(zhí)行完成之后,又自動(dòng)切回了主線程。這就是 Kotlin 協(xié)程給開(kāi)發(fā)做并發(fā)編程帶來(lái)的好處。這也是有個(gè)概念的來(lái)源: Kotlin 協(xié)程同步非阻塞。

同步非阻塞”是真的“同步非阻塞” 嗎?下面探究一下其中的貓膩,通過(guò) Android Studio ,查看 .class 文件中的上面一段代碼:

      BuildersKt.launch$default((CoroutineScope)GlobalScope.INSTANCE, (CoroutineContext)Dispatchers.getMain(), (CoroutineStart)null, (Function2)(new Function2((Continuation)null) {
         int I$0;
         int label;
         @Nullable
         public final Object invokeSuspend(@NotNull Object $result) {
            Object var10000;
            int numbersTo50Sum;
            label17: {
               Object var5 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
               Function2 var10001;
               CoroutineContext var6;
               switch(this.label) {
               case 0:
                  ResultKt.throwOnFailure($result);
                  Log.d("TestCoroutine", "launch start: " + Thread.currentThread());
                  var6 = (CoroutineContext)Dispatchers.getIO();
                  var10001 = (Function2)(new Function2((Continuation)null) {
                     int label;
                     @Nullable
                     public final Object invokeSuspend(@NotNull Object $result) {
                        Object var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
                        switch(this.label) {
                        case 0:
                           ResultKt.throwOnFailure($result);
                           Log.d("TestCoroutine", "launch:numbersTo50Sum: " + Thread.currentThread());
                           this.label = 1;
                           if (DelayKt.delay(1000L, this) == var4) {
                              return var4;
                           }
                           break;
                        case 1:
                           ResultKt.throwOnFailure($result);
                           break;
                        default:
                           throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
                        }
                        Sequence naturalNumbers = SequencesKt.generateSequence(Boxing.boxInt(0), (Function1)null.INSTANCE);
                        Sequence numbersTo50 = SequencesKt.takeWhile(naturalNumbers, (Function1)null.INSTANCE);
                        return Boxing.boxInt(SequencesKt.sumOfInt(numbersTo50));
                     }
                     @NotNull
                     public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
                        Intrinsics.checkNotNullParameter(completion, "completion");
                        Function2 var3 = new <anonymous constructor>(completion);
                        return var3;
                     }
                     public final Object invoke(Object var1, Object var2) {
                        return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
                     }
                  });
                  this.label = 1;
                  var10000 = BuildersKt.withContext(var6, var10001, this);
                  if (var10000 == var5) {
                     return var5;
                  }
                  break;
               case 1:
                  ResultKt.throwOnFailure($result);
                  var10000 = $result;
                  break;
               case 2:
                  numbersTo50Sum = this.I$0;
                  ResultKt.throwOnFailure($result);
                  var10000 = $result;
                  break label17;
               default:
                  throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
               }
               numbersTo50Sum = ((Number)var10000).intValue();
               var6 = (CoroutineContext)Dispatchers.getIO();
               var10001 = (Function2)(new Function2((Continuation)null) {
                  int label;
                  @Nullable
                  public final Object invokeSuspend(@NotNull Object $result) {
                     Object var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
                     switch(this.label) {
                     case 0:
                        ResultKt.throwOnFailure($result);
                        Log.d("TestCoroutine", "launch:numbers50To100Sum: " + Thread.currentThread());
                        this.label = 1;
                        if (DelayKt.delay(1000L, this) == var4) {
                           return var4;
                        }
                        break;
                     case 1:
                        ResultKt.throwOnFailure($result);
                        break;
                     default:
                        throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
                     }
                     Sequence naturalNumbers = SequencesKt.generateSequence(Boxing.boxInt(51), (Function1)null.INSTANCE);
                     Sequence numbers50To100 = SequencesKt.takeWhile(naturalNumbers, (Function1)null.INSTANCE);
                     return Boxing.boxInt(SequencesKt.sumOfInt(numbers50To100));
                  }
                  @NotNull
                  public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
                     Intrinsics.checkNotNullParameter(completion, "completion");
                     Function2 var3 = new <anonymous constructor>(completion);
                     return var3;
                  }
                  public final Object invoke(Object var1, Object var2) {
                     return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
                  }
               });
               this.I$0 = numbersTo50Sum;
               this.label = 2;
               var10000 = BuildersKt.withContext(var6, var10001, this);
               if (var10000 == var5) {
                  return var5;
               }
            }
            int numbers50To100Sum = ((Number)var10000).intValue();
            int result = numbersTo50Sum + numbers50To100Sum;
            Log.d("TestCoroutine", "launch end:result=" + result + ' ' + Thread.currentThread());
            return Unit.INSTANCE;
         }
         @NotNull
         public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
            Intrinsics.checkNotNullParameter(completion, "completion");
            Function2 var3 = new <anonymous constructor>(completion);
            return var3;
         }
         public final Object invoke(Object var1, Object var2) {
            return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
         }
      }), 2, (Object)null);
      Log.d("TestCoroutine", "Hello World!," + Thread.currentThread());

雖然上面 .class 文件中的代碼比較復(fù)雜,但是從大體邏輯可以看出,Kotlin 協(xié)程也是通過(guò)回調(diào)接口來(lái)實(shí)現(xiàn)異步操作的,這也解釋了 Kotlin 協(xié)程只是讓代碼邏輯是同步非阻塞,但是實(shí)際上并沒(méi)有,只是 Kotlin 編譯器為代碼做了很多事情,這也是說(shuō) Kotlin 協(xié)程其實(shí)就是一套線程 API 框架的原因。

再看一個(gè)上面例子的變種:

        GlobalScope.launch(Dispatchers.Main) {
            Log.d("TestCoroutine", "launch start: ${Thread.currentThread()}")
            val numbersTo50Sum = async {
                withContext(Dispatchers.IO) {
                    Log.d("TestCoroutine", "launch:numbersTo50Sum: ${Thread.currentThread()}")
                    delay(2000)
                    val naturalNumbers = generateSequence(0) { it + 1 }
                    val numbersTo50 = naturalNumbers.takeWhile { it <= 50 }
                    numbersTo50.sum()
                }
            }
            val numbers50To100Sum = async {
                withContext(Dispatchers.IO) {
                    Log.d("TestCoroutine", "launch:numbers50To100Sum: ${Thread.currentThread()}")
                    delay(500)
                    val naturalNumbers = generateSequence(51) { it + 1 }
                    val numbers50To100 = naturalNumbers.takeWhile { it in 51..100 }
                    numbers50To100.sum()
                }
            }
            // 計(jì)算 1-50 和 51-100 的自然數(shù)和是兩個(gè)并發(fā)操作
            val result = numbersTo50Sum.await() + numbers50To100Sum.await()
            Log.d("TestCoroutine", "launch end:result=$result ${Thread.currentThread()}")
        }
        Log.d("TestCoroutine", "Hello World!,${Thread.currentThread()}")

控制臺(tái)輸出結(jié)果:
2023-01-02 16:32:12.637 13303-13303/com.wangjiang.example D/TestCoroutine: Hello World!,Thread[main,5,main]
2023-01-02 16:32:13.120 13303-13303/com.wangjiang.example D/TestCoroutine: launch start: Thread[main,5,main]
2023-01-02 16:32:14.852 13303-13444/com.wangjiang.example D/TestCoroutine: launch:numbersTo50Sum: Thread[DefaultDispatcher-worker-2,5,main]
2023-01-02 16:32:14.853 13303-13443/com.wangjiang.example D/TestCoroutine: launch:numbers50To100Sum: Thread[DefaultDispatcher-worker-1,5,main]
2023-01-02 16:32:17.462 13303-13303/com.wangjiang.example D/TestCoroutine: launch end:result=5050 Thread[main,5,main]

async 創(chuàng)建了一個(gè)協(xié)程,它讓計(jì)算 1-50 和 51-100 的自然數(shù)和是兩個(gè)并發(fā)操作。上面控制臺(tái)輸出結(jié)果可以看到計(jì)算 1-50 的自然數(shù)和是在線程 Thread[DefaultDispatcher-worker-2,5,main] 中,而計(jì)算 51-100 的自然數(shù)和是在另一個(gè)線程Thread[DefaultDispatcher-worker-1,5,main]中。

從上面的例子,協(xié)程在異步操作,也就是線程切換上:主線程啟動(dòng)子線程執(zhí)行耗時(shí)操作,耗時(shí)操作執(zhí)行完成將結(jié)果更新到主線程的過(guò)程中,代碼邏輯簡(jiǎn)化,可讀性高。

suspend是什么

suspend 直譯就是:掛起

suspend 是 Kotlin 語(yǔ)言中一個(gè) 關(guān)鍵字,用于修飾方法,當(dāng)修飾方法時(shí),表示這個(gè)方法只能被 suspend 修飾的方法調(diào)用或者在協(xié)程中被調(diào)用。

下面看一下將上面代碼案例拆分成幾個(gè) suspend 方法:

    fun getNumbersTo100Sum() {
        GlobalScope.launch(Dispatchers.Main) {
            Log.d("TestCoroutine", "launch start: ${Thread.currentThread()}")
            val result = calcNumbers1To100Sum()
            Log.d("TestCoroutine", "launch end:result=$result ${Thread.currentThread()}")
        }
        Log.d("TestCoroutine", "Hello World!,${Thread.currentThread()}")
    }
    private suspend fun calcNumbers1To100Sum(): Int {
        return calcNumbersTo50Sum() + calcNumbers50To100Sum()
    }
    private suspend fun calcNumbersTo50Sum(): Int {
        return withContext(Dispatchers.IO) {
            Log.d("TestCoroutine", "launch:numbersTo50Sum: ${Thread.currentThread()}")
            delay(1000)
            val naturalNumbers = generateSequence(0) { it + 1 }
            val numbersTo50 = naturalNumbers.takeWhile { it <= 50 }
            numbersTo50.sum()
        }
    }
    private suspend fun calcNumbers50To100Sum(): Int {
        return withContext(Dispatchers.IO) {
            Log.d("TestCoroutine", "launch:numbers50To100Sum: ${Thread.currentThread()}")
            delay(1000)
            val naturalNumbers = generateSequence(51) { it + 1 }
            val numbers50To100 = naturalNumbers.takeWhile { it in 51..100 }
            numbers50To100.sum()
        }
    }

控制臺(tái)輸出結(jié)果:
2023-01-03 14:47:57.047 11349-11349/com.wangjiang.example D/TestCoroutine: Hello World!,Thread[main,5,main]
2023-01-03 14:47:59.311 11349-11349/com.wangjiang.example D/TestCoroutine: launch start: Thread[main,5,main]
2023-01-03 14:47:59.312 11349-11537/com.wangjiang.example D/TestCoroutine: launch:numbersTo50Sum: Thread[DefaultDispatcher-worker-3,5,main]
2023-01-03 14:48:00.336 11349-11535/com.wangjiang.example D/TestCoroutine: launch:numbers50To100Sum: Thread[DefaultDispatcher-worker-1,5,main]
2023-01-03 14:48:01.339 11349-11349/com.wangjiang.example D/TestCoroutine: launch end:result=5050 Thread[main,5,main]

suspend 關(guān)鍵字標(biāo)記方法時(shí),其實(shí)是告訴 Kotlin 從協(xié)程內(nèi)調(diào)用方法。所以這個(gè)“掛起”,并不是說(shuō)方法或函數(shù)被掛起,也不是說(shuō)線程被掛起。

假設(shè)一個(gè)非 suspend 修飾的方法調(diào)用 suspend 修飾的方法會(huì)怎么樣呢?

  private fun calcNumbersTo100Sum(): Int {
        return calcNumbersTo50Sum() + calcNumbers50To100Sum()
    }

此時(shí),編譯器會(huì)提示:

Suspend function 'calcNumbersTo50Sum' should be called only from a coroutine or another suspend function
Suspend function 'calcNumbers50To100' should be called only from a coroutine or another suspend function

下面查看 .class 文件中的上面方法 calcNumbers50To100Sum 代碼:

   private final Object calcNumbers50To100Sum(Continuation $completion) {
      return BuildersKt.withContext((CoroutineContext)Dispatchers.getIO(), (Function2)(new Function2((Continuation)null) {
         int label;
         @Nullable
         public final Object invokeSuspend(@NotNull Object $result) {
            Object var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
            switch(this.label) {
            case 0:
               ResultKt.throwOnFailure($result);
               Log.d("TestCoroutine", "launch:numbers50To100Sum: " + Thread.currentThread());
               this.label = 1;
               if (DelayKt.delay(1000L, this) == var4) {
                  return var4;
               }
               break;
            case 1:
               ResultKt.throwOnFailure($result);
               break;
            default:
               throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
            }
            Sequence naturalNumbers = SequencesKt.generateSequence(Boxing.boxInt(51), (Function1)null.INSTANCE);
            Sequence numbers50To100 = SequencesKt.takeWhile(naturalNumbers, (Function1)null.INSTANCE);
            return Boxing.boxInt(SequencesKt.sumOfInt(numbers50To100));
         }
         @NotNull
         public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
            Intrinsics.checkNotNullParameter(completion, "completion");
            Function2 var3 = new <anonymous constructor>(completion);
            return var3;
         }
         public final Object invoke(Object var1, Object var2) {
            return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
         }
      }), $completion);
   }

可以看到 private suspend fun calcNumbers50To100Sum() 經(jīng)過(guò) Kotlin 編譯器編譯后變成了private final Object calcNumbers50To100Sum(Continuation $completion), suspend 消失了,方法多了一個(gè)參數(shù) Continuation $completion,所以 suspend修飾 Kotlin 的方法或函數(shù),編譯器會(huì)對(duì)此方法做特殊處理。

另外,suspend 修飾的方法,也預(yù)示著這個(gè)方法是 耗時(shí)方法,告訴方法調(diào)用者要使用協(xié)程。當(dāng)執(zhí)行 suspend 方法,也預(yù)示著要切換線程,此時(shí)主線程依然可以繼續(xù)執(zhí)行,而協(xié)程里面的代碼可能被掛起了。

下面再稍為修改 calcNumbers50To100Sum 方法:

   private suspend fun calcNumbers50To100Sum(): Int {
        Log.d("TestCoroutine", "launch:numbers50To100Sum:start: ${Thread.currentThread()}")
        val sum= withContext(Dispatchers.Main) {
            Log.d("TestCoroutine", "launch:numbers50To100Sum: ${Thread.currentThread()}")
            delay(1000)
            val naturalNumbers = generateSequence(51) { it + 1 }
            val numbers50To100 = naturalNumbers.takeWhile { it in 51..100 }
            numbers50To100.sum()
        }
        Log.d("TestCoroutine", "launch:numbers50To100Sum:end: ${Thread.currentThread()}")
        return sum
    }

控制臺(tái)輸出結(jié)果:
2023-01-03 15:28:04.349 15131-15131/com.bilibili.studio D/TestCoroutine: Hello World!,Thread[main,5,main]
2023-01-03 15:28:04.803 15131-15131/com.bilibili.studio D/TestCoroutine: launch start: Thread[main,5,main]
2023-01-03 15:28:04.804 15131-15266/com.bilibili.studio D/TestCoroutine: launch:numbersTo50Sum: Thread[DefaultDispatcher-worker-3,5,main]
2023-01-03 15:28:06.695 15131-15131/com.bilibili.studio D/TestCoroutine: launch:numbers50To100Sum:start: Thread[main,5,main]
2023-01-03 15:28:06.696 15131-15131/com.bilibili.studio D/TestCoroutine: launch:numbers50To100Sum: Thread[main,5,main]
2023-01-03 15:28:07.700 15131-15131/com.bilibili.studio D/TestCoroutine: launch:numbers50To100Sum:end: Thread[main,5,main]
2023-01-03 15:28:07.700 15131-15131/com.bilibili.studio D/TestCoroutine: launch end:result=5050 Thread[main,5,main]

主線程不受協(xié)程線程的影響。

總結(jié)

Kotlin 協(xié)程是一套線程 API 框架,在 Kotlin 語(yǔ)言環(huán)境下使用它做并發(fā)編程比傳統(tǒng) Thread, Executors 和 RxJava 更有優(yōu)勢(shì),代碼邏輯上“同步非阻塞“,而且簡(jiǎn)潔,易閱讀和維護(hù)。

suspend 是 Kotlin 語(yǔ)言中一個(gè)關(guān)鍵字,用于修飾方法,當(dāng)修飾方法時(shí),該方法只能被 suspend 修飾的方法和協(xié)程調(diào)用。此時(shí),也預(yù)示著該方法是一個(gè)耗時(shí)方法,告訴調(diào)用者需要在協(xié)程中使用。

參考文檔:

Android 上的 Kotlin 協(xié)程

Coroutines guide

下一篇,將研究 Kotlin Flow。

到此這篇關(guān)于Kotlin關(guān)于協(xié)程是什么的探究的文章就介紹到這了,更多相關(guān)Kotlin協(xié)程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論