前言
最近用 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 公布!