美文网首页Flutter学习Flutter
Flutter 优化 async 与 isolate

Flutter 优化 async 与 isolate

作者: janiokq | 来源:发表于2019-08-20 11:16 被阅读0次
    image

    原本 Flutter 写起来很开心,任何耗时较长的工作,就放到一个

    Future<T> someFunction(…) async {…}

    中就好,不需要像 Android 里面还需要 rxJava 或是自己建 thread(线程) 來处理。
    Ios中一样是要用 thread(线程) 来处理
    但是后来发现其实没有那么单纯。就像下面這篇文章的标题一样:刚接触 Flutter 的人通常不會管到 asynchronous 的问题,直到 UI 开始变得很卡的时候。

    isolate 是啥

    我们都知道 dart 是单线程异步编程模型 这一点 和js 很像

    我们首先介绍一下 isolate 是什么东西 虽然 dart 是单线程异步模型 但是 dart 是支持(多线程)的在 dart 中 有一个 模块 就是 isolate (隔离) 我们的 程序 就是 运行 在 main isolate 中 而且 这个 isolate (隔离) 和我们 了解的其他语言的 线程 还不一样 它是不共享内存的 而且 在 flutter 中所有的 channel 和原生通信部分的 引用都是在主线程注册的 也就是 main isolate 才能调用到 (需要注意 在 非 main isolate 中无法调用 channel 通信方法 ) isolate 和 isolate 之间的 通信 是通过 ReceivePort 这个类

    那么有啥问题

    在 Dart 中 async 和 Future 无法解决所有耗时的工作。Dart 虽然支持 非同步执行,但其实如果是透过 async keyword 的话,只是把工作丟到同一个 event loop 中, 让他暂时不会卡住目前的工作 , 等到真的轮到它执行的时候 ,如果它真的很耗时,那 main isolate 还是会 freeze(冻结) 住的 (为什么会冻结? 主线程负责 UI的渲染 工作 但是 如果 密集型计算 很耗时 假如 这个计算 占用 1s的时间 你的UI就会卡住1s) 。Dart 主要的 task 都是在 main isolate 中完成的,isolate 像是个 single thread 的 process。如果真的想要让某些工作能夠同时执行,不要卡住 main isolate 的话,就得要自己产生新的 isolate 來执行。但 isolate 又不是那么好写,必须由ReceivePort来传输参数(线程之间的交互通讯)。下面有個小示例:

    Isolate isolate;
    
    void startRealAsyncTask() async {
      // need a ReceivePort to receive messages.
      ReceivePort receivePort= ReceivePort(); 
      isolate = await Isolate.spawn(heavyTask, receivePort.sendPort);
      receivePort.listen((data) {
        stdout.write('RECEIVE: ' + data + ', ');
      });
    }
    
    void heavyTask(SendPort sendPort) {
      // doing something very heavy here.
      String msg = "I'm done";  
      sendPort.send(msg);
    }
    
    void stop() {  
      if (isolate != null) {
          stdout.writeln('killing isolate');
          isolate.kill(priority: Isolate.immediate);
          isolate = null;        
      }  
    }
    
    void main() async {
      stdout.writeln('spawning isolate...');
      await startRealAsyncTask();
      stdout.writeln('press enter key to quit...');
      await stdin.first;
      stop();
      stdout.writeln('goodbye!');
      exit(0);
    }
    

    好在针对一般需要比较多时间执行的工作,Dart 提供了一個比较容易使用的 compute() function,帮助开发者包装自建 isolate 的繁杂流畅。

    以下是个简单的范例。原先第一行的 processImage() 因为需要针对图片的每个 pixel 做处理,所以會很花时间,如果只是单纯用 async 的话,在执行的時候依然會在 main isolate 做,造成 画面(UI刷新)反应很不流畅。将它改成用 compute() 来调用后,Dart 会帮忙产生新的 isolate 同步执行。如此一来画面就不会再卡卡的了。

    static Image processImage(Image srcImage) {
      return srcImage.replaceColor(Colors.white, Colors.transparent);
    }
    
    Image _getTransparentBackgroundImage(Image srcImage) async {
      Image resultImage = await compute(processImage, srcImage);
      return resultImae;
    }
    

    将现有的 async function 改成 compute()调用,只是几行 code 的事,就可以解決在同一个 event loop 中 执行影响其他 task 导致 不流畅 卡顿的问题,但如同上面的例子所示, compute 呼叫的 function 必须是要是top-level function (全局函数)或是 static (静态方法)才行,而且 调用的方法本身 是 不支持 async function 的 已经 大佬 提供 实现方案 但是 在 目前flutter的版本 中 并没有合并 代码 当前 是 1.7版本 ** 实现的 pull地址 **

    请求合并的 核心 代码 如下

    image.png

    测试用例


    image.png

    由于还没有合并到当前flutter 版本中 不建议直接修改flutter 源码虽然(可以 直接改) 所以我自己对 isolate ReceivePort 做了 简单的封装

    实现线程管理器

    import 'dart:isolate';
    typedef LikeCallback = void Function(Object value);
    
    class  ThreadManagement  {
      //entryPoint 必须是静态方法
      static Future<Map>  runtask (void entryPoint(SendPort message), LikeCallback(Object value),{Object parameter})async{
        final response = ReceivePort();
        Isolate  d =  await Isolate.spawn(entryPoint, response.sendPort);
        // 调用sendReceive自定义方法
        if(parameter!=null){
           SendPort sendPort = await response.first;
           ReceivePort receivePort = ReceivePort();
           sendPort.send([parameter, receivePort.sendPort]);
           receivePort.listen((value){
             receivePort.close();
             d.kill();
             LikeCallback(value);
           });
           return {
             'isolate': d,
             "receivePort":receivePort,
           };
        }else{
          response.listen((value){
            response.close();
            d.kill();
            LikeCallback(value);
          });
          return {
            'isolate': d,
            "receivePort":response,
          };
        }
      }
    }
    

    编写 任务函数(仅做参考)

     static void getbannerthread(SendPort port) async {
        var c  =  await HttpManager.isolationnetFetch(
            "http://118.25.61.120/api/v2/banner"
            ,null,
            null, new Options(method: 'GET'));
        port.send(c);
      }
    

    port.send(c); 是回调计算结果

    调用任务

     ThreadManagement.runtask(API.getbannerthread, (value){
          if(value != null){
                 //业务逻辑
          }
        });
    

    带参数任务

      static getVideolisttask(SendPort port) async {
        ReceivePort receivePort =ReceivePort();
        port.send(receivePort.sendPort);
        // 监听外界调用
        await for (var msg in receivePort) {
          Map requestURL =msg[0];
          SendPort callbackPort =msg[1];
          receivePort.close();
          
          
          var res = await HttpManager.isolationnetFetch(
              "http://xxxxxx?type="+requestURL["type"]+"&after="+requestURL["after"]
              ,null,
              null, new Options(method: 'GET'));
    
    
          callbackPort.send(res);
        }
      }
    

    执行带参数的任务

     ThreadManagement.runtask(API.getVideolisttask, (value){
          if(value != null){
    //业务逻辑
          }
        },parameter: {
          "type":"hot",
          "after":"1"
        });
    

    转载:姜姜和张张
    原文地址
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    相关文章

      网友评论

        本文标题:Flutter 优化 async 与 isolate

        本文链接:https://www.haomeiwen.com/subject/jwbusctx.html