序文
最近、Flutter を使用してビジネス コードを書いていましたが、以前は単純なコードを書いていたときには遭遇しなかった、次のような多くの問題に遭遇しました。
- Flutter を使用してネイティブをチューニングする方法
- 状態管理とイベント管理のどちらを選択するか
- 必要なビューを描画する方法
- ...
上記のシナリオの多くには、非同期の知識が含まれます。
Flutter を作成するときは、ファイル処理、ネットワーク リクエスト、画像の読み込みなどの問題を非同期で処理する必要もあります。
1. Flutter の非同期メカニズム
isolate
この用語は、Flutter の初心者にはなじみがないかもしれませんが、実際には Dart のスレッド メカニズムです。
1.シングルスレッドモデル
Dart はシングル スレッド モデルに基づく言語であり、そのスレッド モデルは次のとおりです。
上の図に示すように。最初に左側を見ると、各分離は、2 つのキューで構成されるイベント ループを維持します。
- microtask キュー: 現在のisolate内の優先度の高いタスクのみを処理します
- イベント キュー: クリック イベント、IO イベント、ネットワーク イベントなどに対応する優先度の低いタスク キュー
右の図からわかるように、isolate は最初に microtask キュー内のタスクを実行し、次にイベント キュー内のタスクを処理し、タスクがなくなると、isolate は終了します。
2.メインアイソレート
各アプリケーションは、1 つのメイン アイソレートと 0 - 複数のワーク アイソレートで構成されます。メイン アイソレートは、Android のメイン スレッドと同様に、無限ループになります。
3. Androidスレッド機構との比較
Android のネイティブ スレッド メカニズムと比較して、マルチスレッド共有メモリのセットは Dart では機能しません。
メモリの観点から言えば、それぞれがプロセスisolate
に似て. メモリは独立して分離されており、相互にアクセスすることはできません. だからこそ、ミューテックスやその他のロックについて考える必要はありません.
上記の優先順位を覚えておいてください: マイクロタスク キュー > イベント キュー、コードの学習を始めましょう!
二、Future
Futrue
これは Dart 非同期プログラミングの中核の 1 つであり、すぐには返されない結果を表します。
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 計算()) | Future をイベント キューに入れる |
Future.microtask | Future をマイクロタスク キューに入れる |
Future.sync | Future で完了コードをすぐに実行する |
上記のメソッドは、主に Future の完了のタイミングに影響します。
4.フューチャービルダー
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 が完了したかどうかだけに注意を払う必要があり、主に次の 3 つの状態に注意します。
- ConnectionState.done が成功: Future は正常に完了します
- ConnectionState.done エラー: エラー状態
- 不完全な状態
3.非同期/待機
レスポンシブ プログラミングのフレーバーがFuture
ある、async/await
それは完全なコルーチンのフレーバーです。
async/await
上記のネットワーク リクエストの後にデータベースを格納するなど、同期的な方法で非同期コードを記述できます。
Future<String> solveNetwork() async {
String netStr = await requestNetwork("JiuXin");
String nextStr = await saveInDB(netStr);
return nextStr;
}
4. ストリーミング
Future
非同期イベントを表すために使用される非同期のコアです。
Stream
これは非同期のもう 1 つの核心であり、一連の非同期イベントを表すために使用されます。
1.ストリームを作成する
通常、Stream を作成するには、yield と StreamController を使用する 2 つの方法があります。
1.1利回り
yield メソッドを使用するのは比較的簡単です. たとえば、リクエストの結果を 10 回送信します:
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
この関数はもう少し強力です:
- ストリームの使用がより柔軟に
- 送信データのキャッシュが可能
構造を図に示します。
主な役割は次の 4 つです。
StreamController: Stream プロセス全体を制御する
ストリーム: データ ソース、監視可能、シングル サブスクリプションは 1 回のみ監視可能、ブロードキャスト ストリームは複数回監視可能
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: イベントが正常に送信されました
ConnectionState.done: StreamSink が閉じられたとき
FutureBuilder は、ConnectionState.done の後にデータを受け入れます。
要約する
Dart の非同期メソッドは非常に単純です. 不明な点がある場合は、コメント欄でお会いしましょう.
この記事は、ブログのマルチポスト プラットフォームであるOpenWriteによって公開されています。