前言
最近用 Flutter 写了一段时间的业务代码,遇到了许多之前写简单代码没遇到的问题,比方说:
- 怎么运用 Flutter 调原生
- 怎么挑选状况办理和事情办理
- 怎么画出自己想要的View
- …
上面中的许多场景,都会涉及到异步知识。
咱们在写 Flutter 的时候,也会需求异步处理问题,比方说文件处理、网络恳求、加载图片等。
一、Flutter中的异步机制
isolate
这个词关于 Flutter 新手来说可能有些陌生,它其实是 Dart 中的线程机制。
1. 单线程模型
Dart 是一种根据单线程模型的言语,它的线程模型是这样的:
An image showing Dart’s event loop. Flutter isolate
正如上面图中所表明的那样。先看左面,每个 isolate 维护着一个事情循环,事情循环由两个行列组成:
- microtask queue:只处理当前 isolate 中的使命,优先级高
- event queue:相应点击事情、IO事情、网络事情等的使命行列,优先级低
从右边的图中,咱们能够看出,isolate 会先履行 microtask queue 中的使命,之后才会处理 event queue 中的使命,没有使命今后,isolate 才会完毕。
2. main isolate
每个应用都由 1 个 main isolate 和 0 – 多个 work isolate 组成,main isolate 跟 Android 中的主线程相同,会处于无限循环的状况:
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 有没有完结,首要关心三个状况:
- ConnectionState.done 成功:Future 正常完结
- ConnectionState.done 过错:过错态
- 非完结态
三、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
的功能愈加强一点:
- Stream运用愈加灵敏
- 能够缓存发射的数据
结构如图:
Stream图片
首要分为四个人物:
- StreamController:控制整个 Stream 流程
- Stream:数据源,可被监听,Single-Subscription 只能被监听一次,Broadcast Stream 能够被屡次监听
- StreamSink:用来添加数据的当地
- 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"))
即可,需求留意的是,在 StatefulWidget
的 dispose
周期中,咱们需求取消对应的监听。
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》