前言

最近用 Flutter 写了一段时间的业务代码,遇到了许多之前写简单代码没遇到的问题,比方说:

  • 怎么运用 Flutter 调原生
  • 怎么挑选状况办理和事情办理
  • 怎么画出自己想要的View

必知必会的Flutter异步知识!

上面中的许多场景,都会涉及到异步知识。

咱们在写 Flutter 的时候,也会需求异步处理问题,比方说文件处理、网络恳求、加载图片等。

一、Flutter中的异步机制

isolate 这个词关于 Flutter 新手来说可能有些陌生,它其实是 Dart 中的线程机制。

1. 单线程模型

Dart 是一种根据单线程模型的言语,它的线程模型是这样的:

必知必会的Flutter异步知识!

An image showing Dart’s event loop. Flutter isolate

正如上面图中所表明的那样。先看左面,每个 isolate 维护着一个事情循环,事情循环由两个行列组成:

  1. microtask queue:只处理当前 isolate 中的使命,优先级高
  2. event queue:相应点击事情、IO事情、网络事情等的使命行列,优先级低

从右边的图中,咱们能够看出,isolate 会先履行 microtask queue 中的使命,之后才会处理 event queue 中的使命,没有使命今后,isolate 才会完毕。

2. main isolate

每个应用都由 1 个 main isolate 和 0 – 多个 work isolate 组成,main isolate 跟 Android 中的主线程相同,会处于无限循环的状况:

必知必会的Flutter异步知识!

A figure showing a main isolate, which runs main(), responds to events, and then exits

3. 与 Android 线程机制对比

跟 Android 原生的线程机制对比,多线程共享内存的那一套在 Dart 上行不通了。

从内存这块儿来看,每一个 isolate 更像是一个进程,内存独立隔离,无法相互拜访状况,也正是因为这个,咱们也不用去考虑互斥锁和其他锁的问题。

记住上面的优先级: microtask queue > event queue,咱们开始学习代码!

二、Future

Futrue 是 Dart 异步编程的中心之一,表明**「一个不会当即回来的成果」**。

1. 运用介绍

一般这么运用:

//模仿网络恳求
Future<String>requestNetwork(Stringname){
returnFuture.delayed(Duration(seconds:1),(){
return"Hello,iam$name";
});
}
voiddoSomeThing(){
requestNetwork("JiuXin")
.then((value)=>print(value))
.catchError((error,stackTrace)=>print(stackTrace));
}

咱们用 Future.delayed 延时模仿网络恳求。

上面的代码运用了链式调用,在 then 办法中,咱们能够获取异步回来的成果,在 onError 办法中,咱们能够处理捕获的异常。

2. 处理多个恳求

then 办法是这样的:

Future<R>then<R>(FutureOr<R>onValue(Tvalue),{Function?onError})

意味处理多个接连恳求,咱们能够运用 then 避免陷入回调地狱:

//模仿网络恳求
Future<String>requestNetwork(Stringname){
returnFuture.delayed(Duration(seconds:1),(){
return"Hello,iam$name";
});
}
//模仿数据库操作
Future<String>saveInDB(Stringname){
returnFuture.delayed(Duration(seconds:1),(){
//处理数据库操作
return"saveinDBsuccess,Hello,iam$name";
});
}
voiddoSomeThing(){
requestNetwork("JiuXin")
.then((value)=>saveInDB(value))
.then((value)=>print(value))
.catchError((error,stackTrace)=>print(stackTrace));
}

3. 一些通用 Api

除了上述的 Future.delayed办法,还有一些 factory 办法:

办法 介绍
Future(FutureOr computation()) 将 Future 放入 event queue
Future.microtask 将 Future 放入 microtask queue
Future.sync 马上履行 Future 里边的完结代码

上述的办法首要影响的是 Future 完结的机遇。

4. FutureBuilder

经过 FutureBuilder,咱们能够在 StatelessWidget 展现出不同的状况,仍是上面的代码:

classTestPageextendsStatelessWidget{
constTestPage({Key?key}):super(key:key);
@override
Widgetbuild(BuildContextcontext){
returnScaffold(
appBar:AppBar(
title:Text("TestPage"),
),
body:Center(
child:FutureBuilder<String>(
future:requestNetwork("JiuXin").then((value)=>saveInDB(value)),
builder:(context,snapshot){
if(snapshot.connectionState==ConnectionState.done){
if(snapshot.hasError){
returnText("onError:${snapshot.error}");
}else{
returnText(snapshot.data??"");
}
}else{
returnCircularProgressIndicator();
}
},
),
),
);
}
}

关于 Future 来说,咱们只需关注 Future 有没有完结,首要关心三个状况:

  1. ConnectionState.done 成功:Future 正常完结
  2. ConnectionState.done 过错:过错态
  3. 非完结态

三、async/await

假如说 Future 有响应式编程的那味儿,那么 async/await 就是不折不扣的协程味儿。

经过 async/await 咱们能够运用同步的方法写出异步的代码,比方上述网络恳求后存数据库:

Future<String>solveNetwork()async{
StringnetStr=awaitrequestNetwork("JiuXin");
StringnextStr=awaitsaveInDB(netStr);
returnnextStr;
}

四、Stream

Future 是异步的一个中心,「用来表明一个异步事情」

Stream 则是异步的别的一个中心,「用来表明一系列异步事情」

1. 创建 Stream

创建 Stream 一般有两种方法:运用 yield 和 StreamController。

1.1 yield

运用 yield 方法比较简单,比方我发送十次恳求成果:

Stream<String>createStream()async*{
for(inti=0;i<10;i++){
Stringresult=awaitrequestNetwork("num:$i");
yieldresult;
}
}

留意,办法右边运用的 async* 关键字,而不是 async,恳求的成果运用 yield 发送。

1.2 StreamController

StreamController 的功能愈加强一点:

  1. Stream运用愈加灵敏
  2. 能够缓存发射的数据

结构如图:

必知必会的Flutter异步知识!

Stream图片

首要分为四个人物:

  1. StreamController:控制整个 Stream 流程
  2. Stream:数据源,可被监听,Single-Subscription 只能被监听一次,Broadcast Stream 能够被屡次监听
  3. StreamSink:用来添加数据的当地
  4. StreamSubscription:监听 Stream 生成的对象,可取消

StreamController 运用流程如下,参考 EventBus:

classEventBus{
StreamController_streamController;
StreamControllergetstreamController=>_streamController;
StreamSinkget_streamSink=>_streamController.sink;
//假如想运用Single-Subscription,_streamController=StreamController()
//假如想运用BroadCast,StreamController.broadcast(sync:sync)
EventBus({boolsync=false})
:_streamController=StreamController.broadcast(sync:sync);
EventBus.customController(StreamControllercontroller)
:_streamController=controller;
Stream<T>on<T>(){
if(T==dynamic){
returnstreamController.streamasStream<T>;
}else{
returnstreamController.stream.where((event)=>eventisT).cast<T>();
}
}
voidfire(event){
_streamSink.add(event);
}
voiddestroy(){
_streamController.close();
}
}

运用途:

EventBusbus=EventBus(sync:true);
classRequestEvent{
Stringcontent;
RequestEvent({requiredthis.content});
}
classStatePageextendsStatefulWidget{
constStatePage({Key?key}):super(key:key);
@override
State<StatePage>createState()=>_StatePageState();
}
class_StatePageStateextendsState<StatePage>{
Stringstr="JiuXin";
lateStreamSubscription<RequestEvent>_subscription;
@override
voidinitState(){
super.initState();
_subscription=bus.on<RequestEvent>().listen((event){
setState((){
str=event.content;
});
});
}
@override
Widgetbuild(BuildContextcontext){
returnContainer(
child:Text(str),
);
}
@override
voiddispose(){
super.dispose();
_subscription.cancel();
}
}

在咱们想要的调用点运用 bus.fire(RequestEvent("content")) 即可,需求留意的是,在 StatefulWidgetdispose 周期中,咱们需求取消对应的监听。

Stream 跟 Rx 系列很类似,而且也有许多其他方便的 api 供咱们调用,感兴趣的能够自己看一下。

2. StreamBuilder运用

StreamBuilder 和 FutureBuilder 有点像,又有点不像。

从整个运用流程来说,它们是相同的,关于状况的监听来说,它们是不一致的。

关于上面的 EventBus,同一个页面,假如咱们想在 StreamBuilder 中运用:

classPageOneextendsStatelessWidget{
PageOne({Key?key})
:super(key:key);
@override
Widgetbuild(BuildContextcontext){
returnCenter(
child:StreamBuilder<RequestEvent>(
stream:bus.on<RequestEvent>(),
builder:(context,snapshot){
if(snapshot.hasError){
returnText("onError:${snapshot.error}");
}
switch(snapshot.connectionState){
caseConnectionState.none:
returnText("暂时没有数据哦~");
caseConnectionState.waiting:
returnCircularProgressIndicator();
caseConnectionState.active:
returnText('${snapshot.data?.content??""}');
caseConnectionState.done:
returnText('Stream已关闭');
}
},
),
);
}
}

解释一下:

  • ConnectionState.active:Event 发送成功
  • ConnectionState.done:当 StreamSink 关闭后

FutureBuilder 是在 ConnectionState.done 今后承受数据。

总结

Dart 中的异步方法仍是挺简单的。

假如有疑惑,咱们评论区见。

参考文章:

《Flutter实战第二版》
《十一、全面深化了解Stream》