Flutter 优化 async 与 isolate

image

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

Future 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  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)