前言
最近用 Flutter 写了一段时间的业务代码,遇到了很多之前写简略代码没遇到的问题,比如说:
- 如何应用 Flutter 调原生
- 如何抉择状态治理和事件治理
- 如何画出本人想要的 View
- …
下面中的很多场景,都会波及到异步常识。
咱们在写 Flutter 的时候,也会须要异步解决问题,比如说文件解决、网络申请、加载图片等。
一、Flutter 中的异步机制
isolate
这个词对于 Flutter 老手来说可能有些生疏,它其实是 Dart 中的线程机制。
1. 单线程模型
Dart 是一种基于单线程模型的语言,它的线程模型是这样的:
正如下面图中所示意的那样。先看右边,每个 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 中的主线程一样,会处于有限循环的状态:
main(), responds to events, and then exits” title=”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(String name){return Future.delayed(Duration(seconds: 1), (){return "Hello, i am $name";});
}
void doSomeThing() {requestNetwork("JiuXin")
.then((value) => print(value))
.catchError((error, stackTrace) => print(stackTrace));
}
咱们用 Future.delayed
延时模仿网络申请。
下面的代码应用了链式调用,在 then
办法中,咱们能够获取异步返回的后果,在 onError
办法中,咱们能够解决捕捉的异样。
2. 解决多个申请
then
办法是这样的:
Future<R> then<R>(FutureOr<R> onValue(T value), {Function? onError})
象征解决多个间断申请,咱们能够应用 then
防止陷入回调天堂:
// 模仿网络申请
Future<String> requestNetwork(String name){return Future.delayed(Duration(seconds: 1), (){return "Hello, i am $name";});
}
// 模仿数据库操作
Future<String> saveInDB(String name) {return Future.delayed(Duration(seconds: 1), (){
// 解决数据库操作
return "save in DB success, Hello, i am $name";
});
}
void doSomeThing() {requestNetwork("JiuXin")
.then((value) => saveInDB(value))
.then((value) => print(value))
.catchError((error, stackTrace) => print(stackTrace));
}
3. 一些通用 Api
除了上述的 Future.delayed 办法,还有一些 factory
办法:
办法 | 介绍 |
---|---|
Future(FutureOr<T> computation()) | 将 Future 放入 event queue |
Future.microtask | 将 Future 放入 microtask queue |
Future.sync | 立即执行 Future 外面的实现代码 |
上述的办法次要影响的是 Future 实现的机会。
4. FutureBuilder
通过 FutureBuilder,咱们能够在 StatelessWidget 展现出不同的状态,还是下面的代码:
class TestPage extends StatelessWidget {const TestPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
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) {return Text("onError: ${snapshot.error}");
} else {return Text(snapshot.data ?? "");
}
} else {return CircularProgressIndicator();
}
},
),
),
);
}
}
对于 Future
来说,咱们只有关注 Future 有没有实现,次要关怀三个状态:
- ConnectionState.done 胜利:Future 失常实现
- ConnectionState.done 谬误:谬误态
- 非实现态
三、async/await
如果说 Future
有响应式编程的那味儿,那么 async/await
就是不折不扣的协程味儿。
通过 async/await
咱们能够应用同步的形式写出异步的代码,比方上述网络申请后存数据库:
Future<String> solveNetwork() async {String netStr = await requestNetwork("JiuXin");
String nextStr = await saveInDB(netStr);
return nextStr;
}
四、Stream
Future
是异步的一个外围,用来示意一个异步事件。
Stream
则是异步的另外一个外围,用来示意一系列异步事件。
1. 创立 Stream
创立 Stream 个别有两种形式:应用 yield 和 StreamController。
1.1 yield
应用 yield 形式比较简单,比方我发送十次申请后果:
Stream<String> createStream() async* {for(int i = 0; i < 10; i++) {String result = await requestNetwork("num: $i");
yield result;
}
}
留神,办法左边应用的 async*
关键字,而不是 async
,申请的后果应用 yield
发送。
1.2 StreamController
StreamController
的性能更增强一点:
- Stream 应用更加灵便
- 能够缓存发射的数据
构造如图:
次要分为四个角色:
- StreamController:管制整个 Stream 流程
- Stream:数据源,可被监听,Single-Subscription 只能被监听一次,Broadcast Stream 能够被屡次监听
- StreamSink:用来增加数据的中央
-
StreamSubscription:监听 Stream 生成的对象,可勾销
StreamController 应用流程如下,参考 EventBus:
class EventBus {
StreamController _streamController;
StreamController get streamController => _streamController;
StreamSink get _streamSink => _streamController.sink;
// 如果想应用 Single-Subscription,_streamController = StreamController()
// 如果想应用 BroadCast,StreamController.broadcast(sync: sync)
EventBus({bool sync = false})
: _streamController = StreamController.broadcast(sync: sync);
EventBus.customController(StreamController controller)
: _streamController = controller;
Stream<T> on<T>() {if (T == dynamic) {return streamController.stream as Stream<T>;} else {return streamController.stream.where((event) => event is T).cast<T>();}
}
void fire(event) {_streamSink.add(event);
}
void destroy() {_streamController.close();
}
}
应用处:
EventBus bus = EventBus(sync: true);
class RequestEvent{
String content;
RequestEvent({required this.content});
}
class StatePage extends StatefulWidget {const StatePage({Key? key}) : super(key: key);
@override
State<StatePage> createState() => _StatePageState();
}
class _StatePageState extends State<StatePage> {
String str = "JiuXin";
late StreamSubscription<RequestEvent> _subscription;
@override
void initState() {super.initState();
_subscription = bus.on<RequestEvent>().listen((event) {setState(() {str = event.content;});
});
}
@override
Widget build(BuildContext context) {
return Container(child: Text(str),
);
}
@override
void dispose() {super.dispose();
_subscription.cancel();}
}
在咱们想要的调用点应用 bus.fire(RequestEvent("content"))
即可,须要留神的是,在 StatefulWidget
的 dispose
周期中,咱们须要勾销对应的监听。
Stream 跟 Rx 系列很类似,并且也有很多其余不便的 api 供咱们调用,感兴趣的能够本人看一下。
2. StreamBuilder 应用
StreamBuilder 和 FutureBuilder 有点像,又有点不像。
从整个应用流程来说,它们是一样的,对于状态的监听来说,它们是不统一的。
对于下面的 EventBus,同一个页面,如果咱们想在 StreamBuilder 中应用:
class PageOne extends StatelessWidget {PageOne({Key? key})
: super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: StreamBuilder<RequestEvent>(stream: bus.on<RequestEvent>(),
builder: (context, snapshot) {if (snapshot.hasError) {return Text("onError: ${snapshot.error}");
}
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text("临时没有数据哦~");
case ConnectionState.waiting:
return CircularProgressIndicator();
case ConnectionState.active:
return Text('${snapshot.data?.content ??""}');
case ConnectionState.done:
return Text('Stream 已敞开');
}
},
),
);
}
}
解释一下:
- ConnectionState.active:Event 发送胜利
-
ConnectionState.done:当 StreamSink 敞开后
FutureBuilder 是在 ConnectionState.done 当前承受数据。
总结
Dart 中的异步形式还是挺简略的,如果有纳闷,咱们评论区见。
本文由博客一文多发平台 OpenWrite 公布!