原本 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"
});
网友评论