协程挂起与恢复源码分析以及逆向剖析还原

1.概述

  协程是一种并发设计模式,您可以在 Android 平台上使用它来简化异步执行的代码。协程是在版本 1.3 中添加到 Kotlin 的,它基于来自其他语言的既定概念。

2.协成特点

  协程是我们在 Android 上进行异步编程的推荐解决方案。值得关注的特点包括一下几点:

  • 轻量:可以在单个线程上运行多个线程,由于协成支持挂起,不会使正在运行协成的线程阻塞,挂起比阻塞节省内存。
  • 内置取消协成支持:我们可以在协成运行前取消协成。

3.协成使用

3.1.添加依赖
dependencies {
   implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9")
}
3.2.协程构建器种类
  • 3.2.1.GlobalScope.launch
    特点:全局的协程构建器,并不会阻塞当前线程。它的生命周期随着Application。一般不建议使用。
    注意: 协程是依附于线程的,一旦线程执行完成,协程也就结束了,不会执行了。
//1.全局的协程构建器,并不会阻塞当前线程。
GlobalScope.launch {
    //给定一个延时的时间,同时它不会阻塞线程,会在一定时间后执行协程。
    delay(1000)
    println("Kotlin Coroutines")
}
  • 3.2.2.runBlocking
    特点:协程构建器,它会阻塞当前线程,直到协程执行结束。(一般用于main函数和测试使用)
//它会创建新的协程,并阻塞当前线程。
runBlocking {
    delay(2000)
}
  • 3.2.3.coroutineScope
    特点:它会创建一个新的协程,他并不会阻塞当前线程,并会等待协程执行完成。
    注意:它是一个挂起函数suspend
coroutineScope {
    launch {
        delay(20000)
        println("my job2")
    }
    delay(5000)
    println("hello world")
}
  • 3.2.4.lifecycleScope/viewModelScope
    注意:
      1.lifecycleScope只能在Activity或者Fragment中使用,他会依附与Activity/Fragmen的声明周期。页面销毁,协程也就被销毁了,避免内存泄漏。
      2.viewModelScope只能在ViewModel的子类中使用,他也是随着ViewModel生命周期的,ViewModel销毁了,协程也就销毁了。
需要添加依赖
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version")

//1.lifecycleScope的使用
lifecycleScope.launch {}

lifecycleScope.async {}

//当我们宿主的声明周期至少为onCreate的时候才会启动
lifecycleScope.launchWhenCreated {
    HiLog.e(TAG,"launchWhenCreated")
    whenCreated {
        //这里的代码 只有宿主的声明周期为onCreate才会执行,否则暂停
        HiLog.e(TAG,"whenCreated")
    }
    whenResumed {
        //这里的代码 只有宿主的声明周期为onResume才会执行,否则暂停
        HiLog.e(TAG,"whenResumed")
    }
    whenStarted {
        //这里的代码 只有宿主的声明周期为onStrart才会执行,否则暂停
        HiLog.e(TAG,"whenStarted")
    }
}

//当我们宿主的声明周期至少为onStart的时候才会启动
lifecycleScope.launchWhenStarted {

}

//2.viewModelScope
viewModelScope.launch{}
3.3.启动协成
  • launch:可启动新协程而不将结果返回给调用方。
  • async:会启动一个新的协程,并可以通过 await 的挂起函数返回结果。
 /**
     * TODO:GlobalScope.launch参数
     *      参数2:CoroutineStart启动模式
     *            DEFAULT:默认启动模式,创建即启动协程,可随时取消
     *            LAZY:延时启动模式,只有当我们调用start方法才会启动
     *            ATOMIC:自动模式,同样创建即启动协程,但启动前不能取消
     */
    fun startScene1(){
        HiLog.e(TAG,"coroutine is running")
        //并不会阻塞当前线程。
        val job = GlobalScope.launch(Dispatchers.Main,CoroutineStart.LAZY) {
            val request1 = request1()
            val request2 = request2(request1)
            val request3 = request3(request2)

            updateUI(request3)
        }
        job.start()

        HiLog.e(TAG,"coroutine has launched")
    }


    /**
     * TODO:启动一个线程,先执行request1,再同时启动request2,request3,当request2,request3执行完成后更行UI
     */
    fun startScene2(){
        HiLog.e(TAG,"coroutine is running")
        val job = GlobalScope.launch(Dispatchers.Main) {
            val request1 = request1()
            val deferred2 = GlobalScope.async { request2(request1)}
            val deferred3 = GlobalScope.async { request3(request1)}

            updateUI2(deferred2.await(),deferred3.await())
        }
        job.start()
        HiLog.e(TAG,"coroutine has launched")
    }
3.4.协成的挂起
实例:
object CoroutineScene2{
    private val TAG :String = "TAG"

    suspend fun request1():String{
        val request2 = request2()
        HiLog.e(TAG,"request1 completed")
        return "result from request1 ----------- $request2"
    }

    suspend fun request2():String{
        delay(5 * 1000)
        HiLog.e(TAG,"request2 completed")
        return "result2 from requests"
    }
}
通过上述的示例代码,我们通过反编译后看一下。
public final class CoroutineScene2 {
   private static final String TAG = "TAG";
   @NotNull
   public static final CoroutineScene2 INSTANCE;

   //它是传入一个Continuation接口,
   @Nullable
   public final Object request1(@NotNull Continuation var1) {
        Object $continuation;
         //创建一个ContinuationImpl抽象类,将其传入的Continuation接口对象传入,
         $continuation = new ContinuationImpl(var1) {
            // $FF: synthetic field
            Object result;
            int label;    //默认为0

            @Nullable
            public final Object invokeSuspend(@NotNull Object $result) {
               this.result = $result;
               this.label |= Integer.MIN_VALUE;
               return CoroutineScene2.this.request1(this);
            }
         };
      }

      Object $result = (()$continuation).result;
      Object var5 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
      Object var10000;
      switch((()$continuation).label) {
      case 0:  //如果label为0,进入,所以第一次都会进入
         ResultKt.throwOnFailure($result);
         //将其赋值为1
         (()$continuation).label = 1;
         //调用request2方法,传入的是上面创建的ContinuationImpl
         var10000 = this.request2((Continuation)$continuation);
         //判断当前状态是否为SUSPENDED,直接return。
         if (var10000 == var5) {
            return var5;
         }
         break;
      case 1:
         ResultKt.throwOnFailure($result);
         var10000 = $result;
         break;
      default:
         throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
      }

      String request2 = (String)var10000;
      HiLog.e(new Object[]{TAG, "request1 completed"});
      return "result from request1 ----------- " + request2;
   }

1.通过反编译后查看其实协程的挂起与恢复核心就是通过return 和 Callback来进行实现。
2.当我们一个函数被suspend关键字修饰,那么它并不一定会被挂起,需要根据挂起函数内的执行代码,如果函数的返回值为COROUTINE_SUSPENDED,那么就会直接return。所以后面代码不会被执行。否则则会通过callback进行回掉,在次调用该方法。
通过Java代码还原协成的挂起与恢复。采用return + Callback的方法。

/**
 * TODO:Java代码实现coroutine的挂起函数
 *   当我们将协程代码反编译后,它就是通过return结合callback进行实现的。
 *
 *     suspend fun request1(request2:String): String {
 *         delay(2 * 1000)
 *         HiLog.e(TAG,"request1 work on ${Thread.currentThread().name}")
 *         return "result from request1"
 *     }
 *
 *    suspend fun request2(): String {
 *         //delay方法并不会暂停线程,但是会暂停当前所在的协程
 *         delay(2 * 1000)
 *         HiLog.e("TAG","request2 work on ${Thread.currentThread().name}")
 *         return "result from request2"
 *     }
 */
public class CoroutineScene2_decompiled {

    public static final Object request1(Continuation preCallback){
        /**
         * TODO:注册一个回调
         */
        ContinuationImpl request1Callback;
        //判断一下是否已经包装过,就是为了防止恢复的时候再次包装。
        if (!(preCallback instanceof ContinuationImpl) || (((ContinuationImpl) preCallback).label & Integer.MIN_VALUE) == 0){
            request1Callback = new ContinuationImpl(preCallback){
                @Override
                Object invokeSuspend(@NotNull Object resumeResult) {
                    this.result = resumeResult;
                    this.label |= Integer.MIN_VALUE;
                    HiLog.e("TAG","request1 has resume");
                    //恢复当前挂起函数
                    return request1(this);
                }
            };
        }else {
            request1Callback = (ContinuationImpl) preCallback;
        }

        /**
         * TODO:进行判断状态
         */
        switch (request1Callback.label){
            case 0: //创建的回调,默认为0.
                Object request2 = request2(request1Callback);
                if (request2 == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
                    HiLog.e("TAG","request1 has suspend");
                    return IntrinsicsKt.getCOROUTINE_SUSPENDED();
                }
                break;
        }
        HiLog.e("TAG","request1 work on "+Thread.currentThread().getName());
        return "result from request1 ----------- " + request1Callback.result;
    }



    public static final Object request2(Continuation preCallback){
        /**
         * TODO:注册一个回调
         */
        ContinuationImpl request2Callback;
        //判断一下是否已经包装过,就是为了防止恢复的时候再次包装。
        if (!(preCallback instanceof ContinuationImpl) || (((ContinuationImpl) preCallback).label & Integer.MIN_VALUE) == 0){
            request2Callback = new ContinuationImpl(preCallback){
                @Override
                Object invokeSuspend(@NotNull Object resumeResult) {
                    this.result = resumeResult;
                    this.label |= Integer.MIN_VALUE;
                    //恢复当前挂起函数
                    HiLog.e("TAG","request2 has resume");
                    return request2(this);
                }
            };
        }else {
            request2Callback = (ContinuationImpl) preCallback;
        }

        /**
         * TODO:进行判断状态
         */
        switch (request2Callback.label){
            case 0: //创建的回调,默认为0.
                Object delay = DelayKt.delay(2000, request2Callback);
                if (delay == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
                    HiLog.e("TAG","request2 has suspend");
                    return IntrinsicsKt.getCOROUTINE_SUSPENDED();
                }
                break;
        }

        HiLog.e("TAG","request2 work on "+Thread.currentThread().getName());
        return "result from request2";
    }


    static abstract class ContinuationImpl implements Continuation{
        private Continuation preCallback;
        Object result;
        int label;

        public ContinuationImpl(Continuation preCallback) {
            this.preCallback = preCallback;
        }

        @NotNull
        @Override
        public CoroutineContext getContext() {
            return preCallback.getContext();
        }

        /**
         * TODO:该函数就是被挂起函数恢复时所返回的值
         * @param resumeResult
         */
        @Override
        public void resumeWith(@NotNull Object resumeResult) {
            Object suspend = invokeSuspend(resumeResult);
            //1.再次判断当前值是否还等于SUSPENDED,如果还等于表示你虽然恢复了,但是又被挂起了
            if (suspend == IntrinsicsKt.getCOROUTINE_SUSPENDED()){
                return;
            }
            //2.此时就是真正的恢复了,调用我们包装传入的Continuation回调的resumeResult。
            preCallback.resumeWith(suspend);
        }

        abstract Object invokeSuspend(@NotNull Object resumeResult);
    }
}

4.协成分析

//CoroutineScope.launch不会阻塞当前线程
public fun CoroutineScope.launch(
    //1.协程上下文,也就是协程调度器,如果不指定,默认为Default,也就是IO
        Dispatchers.IO:子线程  
        Dispatchers.Main:主线程
        Dispatchers.Unconfined
    context: CoroutineContext = EmptyCoroutineContext,
    //2.协程启动模式:
        CoroutineStart.DEFAULT:默认会创建即启动协程,可随时取消
        CoroutineStart.LAZY   :延时启动模式,当我们手动调用start()方法实例启动协程
        CoroutineStart.ATOMIC :自动模式,同样创建即启动协程,但启动前不能取消
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {
    //如果不指定调度器,默认会为我们创建一个Default,也就是IO
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
        LazyStandaloneCoroutine(newContext, block) else
        StandaloneCoroutine(newContext, active = true)
    //启动协程  
    coroutine.start(start, coroutine, block)
    return coroutine
}

你可能感兴趣的:(协程挂起与恢复源码分析以及逆向剖析还原)