Flutter での非同期的な方法

序文

最近、Flutter を使用してビジネス コードを書いていましたが、以前は単純なコードを書いていたときには遭遇しなかった、次のような多くの問題に遭遇しました。

  • Flutter を使用してネイティブをチューニングする方法
  • 状態管理とイベント管理のどちらを選択するか
  • 必要なビューを描画する方法
  • ...

表情包

上記のシナリオの多くには、非同期の知識が含まれます。

Flutter を作成するときは、ファイル処理、ネットワーク リクエスト、画像の読み込みなどの問題を非同期で処理する必要もあります。

1. Flutter の非同期メカニズム

isolateこの用語は、Flutter の初心者にはなじみがないかもしれませんが、実際には Dart のスレッド メカニズムです。

1.シングルスレッドモデル

Dart はシングル スレッド モデルに基づく言語であり、そのスレッド モデルは次のとおりです。

Dart のイベント ループを示すイメージ。 フラッターアイソレート

上の図に示すように。最初に左側を見ると、各分離は、2 つのキューで構成されるイベント ループを維持します。

  1. microtask キュー: 現在のisolate内の優先度の高いタスクのみを処理します
  2. イベント キュー: クリック イベント、IO イベント、ネットワーク イベントなどに対応する優先度の低いタスク キュー

右の図からわかるように、isolate は最初に microtask キュー内のタスクを実行し、次にイベント キュー内のタスクを処理し、タスクがなくなると、isolate は終了します。

2.メインアイソレート

各アプリケーションは、1 つのメイン アイソレートと 0 - 複数のワーク アイソレートで構成されます。メイン アイソレートは、Android のメイン スレッドと同様に、無限ループになります。

`main()` を実行し、イベントに応答して終了するメイン アイソレートを示す図

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 つの状態に注意します。

  1. ConnectionState.done が成功: Future は正常に完了します
  2. ConnectionState.done エラー: エラー状態
  3. 不完全な状態

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この関数はもう少し強力です:

  1. ストリームの使用がより柔軟に
  2. 送信データのキャッシュが可能

構造を図に示します。

ストリーム画像

主な役割は次の 4 つです。

  1. StreamController: Stream プロセス全体を制御する

  2. ストリーム: データ ソース、監視可能、シングル サブスクリプションは 1 回のみ監視可能、ブロードキャスト ストリームは複数回監視可能

  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"))だけです.のサイクル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によって公開されています。

おすすめ

転載: blog.csdn.net/qq_35063091/article/details/127356382