はじめに
DartのStreamは、非同期データのフローを処理するための強力なツールです。データが逐次的に流れる場合(リアルタイムデータ、イベント処理、ネットワーク通信など)、Streamを使うことで効率的にデータを扱うことができます。Streamは、リスナーに対してデータやエラーを非同期に提供し、リアルタイムアプリケーションで頻繁に利用されます。
この記事では、Dart公式ドキュメントを基に、Streamの作成方法や使い方の基本から応用例までを解説します。Flutterアプリケーションでも活用できるStreamの使い方をマスターし、効率的なリアルタイムデータ処理を実現しましょう。
Streamとは
Streamは、非同期に一連のデータを受信するためのDartのデータ型です。データはイベントの形で連続的に提供され、各イベントがリスナーによって処理されます。Streamを使用することで、データのリアルタイム処理や非同期処理を効率的に行うことができます。
Streamの主な特徴
- 非同期データの処理:非同期に到着するデータをリアルタイムで処理します。
- リスナーを設定可能:複数のリスナーがStreamのデータを受け取ることができます。
- 単一購読(Single subscription)とブロードキャスト(Broadcast)の2つのモードをサポート。
Streamの作成方法
Dartでは、Streamを作成する方法がいくつかあります。以下のセクションで、それぞれの方法について詳しく説明します。
1. StreamControllerを使ったStreamの作成
StreamControllerを使うことで、Streamにデータを追加したり、リスナーに通知を送ることができます。
import 'dart:async';
void main() {
  // StreamControllerの作成
  final controller = StreamController<int>();
  // リスナーを追加
  controller.stream.listen((data) {
    print('Received: $data');
  });
  // データをストリームに追加
  controller.add(1);
  controller.add(2);
  controller.add(3);
  // StreamControllerを閉じる
  controller.close();
}解説
- StreamController:Streamを作成するためのコントローラー。- addメソッドを使ってデータを追加できます。
- controller.stream.listen:リスナーを追加し、Streamからデータを受信します。
2. 非同期ジェネレーター(async*)を使ったStreamの作成
async*とyieldを使うことで、非同期にデータを生成するStreamを簡単に作成できます。
Stream<int> countStream(int max) async* {
  for (int i = 1; i <= max; i++) {
    // 非同期で値を出力
    yield i;
    await Future.delayed(Duration(seconds: 1)); // 1秒待機
  }
}
void main() {
  // 非同期ジェネレーターで生成したStreamをリスン
  countStream(5).listen((data) {
    print('Received: $data');
  });
}解説
- async*と- yield:非同期にデータを順次出力するために使用します。
- await Future.delayed:データを出力する前に1秒の遅延を設定。
ちなみにasync/awaitについては以下の記事で書いていますので、良かったら見てみてください。
3. Stream.periodicを使ったStreamの作成
Stream.periodicを使うと、指定した間隔でデータを生成するStreamを作成できます。
void main() {
  // 1秒ごとにデータを生成するStream
  final periodicStream = Stream<int>.periodic(
    Duration(seconds: 1),
    (count) => count,
  );
  // リスナーを追加してデータを受信
  final subscription = periodicStream.listen((data) {
    print('Periodic data: $data');
  });
  // 5秒後にストリームをキャンセル
  Future.delayed(Duration(seconds: 5), () {
    subscription.cancel();
    print('Stream cancelled');
  });
}解説
- Stream.periodic:一定の間隔でデータを生成するためのStream。
- subscription.cancel:Streamの購読をキャンセルするためのメソッド。
Streamのタイプ
1. 単一購読のStream(Single-subscription Stream)
単一購読のStreamは、一度に1つのリスナーのみが購読できます。通常、ファイル読み込みやHTTPリクエストの結果を処理する場合に使用されます。
2. ブロードキャストのStream(Broadcast Stream)
ブロードキャストStreamは、複数のリスナーが同時に購読できるStreamです。イベントの通知など、複数のリスナーにデータを送る場合に適しています。
void main() {
  // ブロードキャストStreamの作成
  final controller = StreamController<int>.broadcast();
  // 複数のリスナーを追加
  controller.stream.listen((data) {
    print('Listener 1: $data');
  });
  controller.stream.listen((data) {
    print('Listener 2: $data');
  });
  // データをストリームに追加
  controller.add(1);
  controller.add(2);
  controller.add(3);
  // StreamControllerを閉じる
  controller.close();
}解説
- StreamController<int>.broadcast:ブロードキャストモードのStreamを作成します。
- 複数のリスナーが同時にデータを受信可能。
Streamのエラーハンドリング
Streamを使う際には、エラーが発生する可能性があります。onErrorを使ってエラーを処理することができます。
import 'dart:async';
void main() {
  final controller = StreamController<int>();
  controller.stream.listen(
    (data) {
      print('Received: $data');
    },
    onError: (error) {
      print('Error occurred: $error');
    },
    onDone: () {
      print('Stream closed');
    },
  );
  // データを追加
  controller.add(1);
  controller.addError('This is an error'); // エラーを追加
  controller.add(3);
  // ストリームを閉じる
  controller.close();
}解説
- addError:Streamにエラーを送信するメソッド。
- onError:エラーハンドリング用のコールバック。
Streamのベストプラクティス
- リソースの管理- Streamを使う際には、StreamControllerを適切に閉じる(closeメソッドを呼ぶ)ことが重要です。これにより、リソースリークを防ぎます。
 
- Streamを使う際には、
- Streamのキャンセル- 購読を終了したい場合には、cancelメソッドを使ってStreamの購読をキャンセルすることが推奨されます。特にブロードキャストStreamでは、購読のキャンセルが必要です。
 
- 購読を終了したい場合には、
- エラーハンドリングを徹底する- Streamを使用する際には、常にエラーハンドリングを行い、予期しないエラーがアプリケーション全体に影響を与えないようにしましょう。
 
まとめ
DartのStreamを活用することで、非同期データの処理やリアルタイムアプリケーションの開発が容易になります。Streamを使うと、データのフローを管理しやすく、リスナーを設定することで効率的にデータを処理できます。また、非同期ジェネレーターやStream.periodicを使って簡単に非同期データを生成できるため、様々なシチュエーションに対応可能です。
この記事を参考にして、DartのStreamを活用してリアルタイム処理を効率化し、Flutterアプリケーションのパフォーマンスを向上させましょう!

 
 



コメント