乐趣区

关于android:Flutter中的异步方式

前言

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

  • 如何应用 Flutter 调原生
  • 如何抉择状态治理和事件治理
  • 如何画出本人想要的 View

下面中的很多场景,都会波及到异步常识。

咱们在写 Flutter 的时候,也会须要异步解决问题,比如说文件解决、网络申请、加载图片等。

一、Flutter 中的异步机制

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

1. 单线程模型

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

正如下面图中所示意的那样。先看右边,每个 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 中的主线程一样,会处于有限循环的状态:

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 有没有实现,次要关怀三个状态:

  1. ConnectionState.done 胜利:Future 失常实现
  2. ConnectionState.done 谬误:谬误态
  3. 非实现态

三、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 的性能更增强一点:

  1. Stream 应用更加灵便
  2. 能够缓存发射的数据

构造如图:

次要分为四个角色:

  1. StreamController:管制整个 Stream 流程
  2. Stream:数据源,可被监听,Single-Subscription 只能被监听一次,Broadcast Stream 能够被屡次监听
  3. StreamSink:用来增加数据的中央
  4. 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")) 即可,须要留神的是,在 StatefulWidgetdispose 周期中,咱们须要勾销对应的监听。

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 公布!

退出移动版