草庐IT

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

如愿以偿丶 2023-03-28 原文

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 = ((<undefinedtype>)$continuation).result;
      Object var5 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
      Object var10000;
      switch(((<undefinedtype>)$continuation).label) {
      case 0:  //如果label为0,进入,所以第一次都会进入
         ResultKt.throwOnFailure($result);
         //将其赋值为1
         ((<undefinedtype>)$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<T> implements Continuation<T>{
        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
}

有关协程挂起与恢复源码分析以及逆向剖析还原的更多相关文章

  1. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  2. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  3. UE4 源码阅读:从引擎启动到Receive Begin Play - 2

    一、引擎主循环UE版本:4.27一、引擎主循环的位置:Launch.cpp:GuardedMain函数二、、GuardedMain函数执行逻辑:1、EnginePreInit:加载大多数模块int32ErrorLevel=EnginePreInit(CmdLine);PreInit模块加载顺序:模块加载过程:(1)注册模块中定义的UObject,同时为每个类构造一个类默认对象(CDO,记录类的默认状态,作为模板用于子类实例创建)(2)调用模块的StartUpModule方法2、FEngineLoop::Init()1、检查Engine的配置文件找出使用了哪一个GameEngine类(UGame

  4. 阿里云国际版免费试用:如何注册以及注意事项 - 2

    作为新的阿里云用户,您可以50免费试用多种优惠,价值高达1,700美元(或8,500美元)。这将让您了解和体验阿里云平台上提供的一系列产品和服务。如果您以个人身份注册免费试用,您将获得价值1,700美元的优惠。但是,如果您是注册公司,您可以选择企业免费试用,提交基本信息通过企业实名注册验证,即可开始价值$8,500的免费试用!本教程介绍了如何设置您的帐户并使用您的免费试用版。​关于免费试用在我们开始此试用之前,您还必须遵守以下条款和条件才能访问您的免费试用:只有在一年内创建的账户才有资格获得阿里云免费试用。通过此免费试用优惠,用户可以免费试用免费试用活动页面上列出的每种产品一次。如果您有多个帐

  5. ruby - ruby 中的同一个程序如何接受来自用户的输入以及命令行参数 - 2

    我的ruby​​脚本从命令行参数获取某些输入。它检查是否缺少任何命令行参数,然后提示用户输入。但是我无法使用gets从用户那里获得输入。示例代码:test.rbname=""ARGV.eachdo|a|ifa.include?('-n')name=aputs"Argument:#{a}"endendifname==""puts"entername:"name=getsputsnameend运行脚本:rubytest.rbraghav-k错误结果:test.rb:6:in`gets':Nosuchfileordirectory-raghav-k(Errno::ENOENT)fromtes

  6. 建模分析 | 平面2R机器人(二连杆)运动学与动力学建模(附Matlab仿真) - 2

    目录0专栏介绍1平面2R机器人概述2运动学建模2.1正运动学模型2.2逆运动学模型2.3机器人运动学仿真3动力学建模3.1计算动能3.2势能计算与动力学方程3.3动力学仿真0专栏介绍?附C++/Python/Matlab全套代码?课程设计、毕业设计、创新竞赛必备!详细介绍全局规划(图搜索、采样法、智能算法等);局部规划(DWA、APF等);曲线优化(贝塞尔曲线、B样条曲线等)。?详情:图解自动驾驶中的运动规划(MotionPlanning),附几十种规划算法1平面2R机器人概述如图1所示为本文的研究本体——平面2R机器人。对参数进行如下定义:机器人广义坐标

  7. 网站日志分析软件--让网站日志分析工作变得更简单 - 2

    网站的日志分析,是seo优化不可忽视的一门功课,但网站越大,每天产生的日志就越大,大站一天都可以产生几个G的网站日志,如果光靠肉眼去分析,那可能看到猴年马月都看不完,因此借助网站日志分析工具去分析网站日志,那将会使网站日志分析工作变得更简单。下面推荐两款网站日志分析软件。第一款:逆火网站日志分析器逆火网站日志分析器是一款功能全面的网站服务器日志分析软件。通过分析网站的日志文件,不仅能够精准的知道网站的访问量、网站的访问来源,网站的广告点击,访客的地区统计,搜索引擎关键字查询等,还能够一次性分析多个网站的日志文件,让你轻松管理网站。逆火网站日志分析器下载地址:https://pan.baidu.

  8. elasticsearch源码关于TransportSearchAction【阶段三】 - 2

    1.回顾.TransportServicepublicclassTransportServiceextendsAbstractLifecycleComponentTransportService:方法:1publicfinalTextendsTransportResponse>voidsendRequest(finalTransport.Connectionconnection,finalStringaction,finalTransportRequestrequest,finalTransportRequestOptionsoptions,TransportResponseHandlerT>

  9. (附源码)vue3.0+.NET6实现聊天室(实时聊天SignalR) - 2

    参考文章搭建文章gitte源码在线体验可以注册两个号来测试演示图:一.整体介绍  介绍SignalR一种通讯模型Hub(中心模型,或者叫集线器模型),调用这个模型写好的方法,去发送消息。  内容有:    ①:Hub模型的方法介绍    ②:服务器端代码介绍    ③:前端vue3安装并调用后端方法    ④:聊天室样例整体流程:1、进入网站->调用连接SignalR的方法2、与好友发送消息->调用SignalR的自定义方法 前端通过,signalR内置方法.invoke()  去请求接口3、监听接受方法(渲染消息)通过new signalR.HubConnectionBuilder().on

  10. ABB-IRB-1200运动学分析MATLAB RVC工具分析+Simulink-Adams联合仿真 - 2

    一、机器人介绍        此处是基于MATLABRVC工具箱,对ABB-IRB-1200型号的微型机械臂进行正逆向运动学分析,并利Simulink工具实现对机械臂进行具有动力学参数的末端轨迹规划仿真,最后根据机械模型设计Simulink-Adams联合仿真。 图1.ABBIRB 1200尺寸参数示意图ABBIRB 1200提供的两种型号广泛适用于各作业,且两者间零部件通用,两种型号的工作范围分别为700 mm 和 900 mm,大有效负载分别为 7 kg 和5 kg。 IRB 1200 能够在狭小空间内能发挥其工作范围与性能优势,具有全新的设计、小型化的体积、高效的性能、易于集成、便捷的接

随机推荐