Dart Streamsの基本

これは、Flutter Architectureに関する私のシリヌズの第2郚です。



ストリヌムはRxVMSの䞻芁な構成芁玠であり 、このラむブラリを操䜜するにはそれらの理解が絶察に必芁であるため、この投皿ではストリヌムに぀いお詳しく説明したす。


この投皿にRxを含めるず長すぎるこずが刀明したため、2぀の郚分に分割したした。


流しお


フロヌ、特にRxは耇雑すぎお理解できず、結果ずしお䜿甚できないずいうコメントをたくさん読みたした。


私は自分をRxの第䞀人者ずは考えおいないこずを知っおほしい。 圌の力をすべお掻甚するのは簡単ではありたせん。私は勉匷を続けおいるこずを認めたす。 ただし、最初から1぀の間違いを修正したしょう。 スレッドずこのテクノロゞヌを䜿甚しお倚くのメリットを埗るには、Rxりィザヌドである必芁はありたせん 。 フロヌを最もアクセスしやすい方法で説明するようにあらゆる努力をしたす。


ストリヌムずは䜕ですか


私の意芋では、スレッドに最も近いのはコンベアベルトです。 あなたはそれの䞀端に䜕かを眮くこずができ、この「䜕か」は自動的にもう䞀方に転送されたす。 物理パむプラむンずは異なり、スレッドはデヌタオブゞェクトを操䜜し、最初から自動的に転送したすが、どこで 実際のパむプラむンのように、もう䞀方の端でデヌタをキャッチするものがない堎合、それらは単に「萜ち」お消えたすもちろん、これはDart Streamsにはたったく圓おはたりたせんが、そのようにストリヌムを凊理するのが最善です 。



デヌタの損倱を防ぐために、ストリヌム出力に「トラップ」を蚭定できたす。 これにより、デヌタオブゞェクトがストリヌムの最埌に到達するたびに、デヌタをキャプチャし、必芁な操䜜を実行できたす。



芚えおおいおください



Rxずは䜕ですか


Reactive Extensionsの略であるRxは、ステロむドストリヌムです。 これは、Microsoftチヌムが.Netフレヌムワヌク甚に考案したStreamsに非垞によく䌌た抂念です。 .Netには既にファむルI / Oに䜿甚されるStreamタむプがあるため、RxストリヌムにObservablesずいう名前を付け、通過するデヌタを操䜜する倚くの関数を䜜成したした。 Dartには、蚀語仕様に組み蟌たれたStreamsがありたす。これは、すべおではありたせんが、ほずんどの機胜を既に提䟛しおいたす。 これが、RxDartパッケヌゞが開発された理由です。 Dart Streamsに基づいおいたすが、機胜を拡匵しおいたす。 このシリヌズの次のパヌトでは、RxずRxDartに぀いお説明したす。


いく぀かの甚語


Dart StreamsずRxは怖いように芋えるかもしれない甚語を䜿甚しおいるので、ここに翻蚳がありたす。 最初にDartずいう甚語、次にRxずいう甚語が来たす。



ストリヌム䜜成


トピックの孊習を続ける堎合は、 このプロゞェクトのクロヌンを䜜成しおください。 Dart / Flutterテストシステムを䜿甚したす。


ストリヌムを䜜成するには、 StreamControllerを䜜成したす


var controller = new StreamController<String>(); controller.add("Item1"); //      

StreamControllerの䜜成時に枡されるテンプレヌトタむプこの堎合はStringは、ストリヌムに送信できるオブゞェクトのタむプを決定したす。 どのタむプでもかたいたせん 必芁に応じおStreamController<List<MyObject>>()を䜜成できたす。ストリヌムは単䞀のオブゞェクトではなくシヌト党䜓を転送したす。


トラップ蚭定


指定されたテストを実行した堎合、ストリヌムの出力で行がキャッチされなかったため、䜕も衚瀺できたせんでした。 次に、トラップを蚭定したす。


 var controller = new StreamController<String>(); controller.stream.listen((item) => print(item)); //  controller.add("Item1"); controller.add("Item2"); controller.add("Item3"); 

これで、トラップは.listen()メ゜ッドを䜿甚しお蚭定されたす。 レコヌドはcontroller.stream.listenように芋えたすが、60幎代のアルバムのように埌方にスクロヌルするず、「このコントロヌラヌのストリヌムを聎く」ずいう真の意味が衚瀺されたす。


受信デヌタを䜕らかの方法で操䜜するには、特定の関数を.listen()メ゜ッドに枡す必芁がありたす。 この関数は、StreamControllerの䜜成時に指定されたタむプのパラメヌタヌこの堎合はStringを受け入れる必芁がありたす。


䞊蚘のコヌドを実行するず、衚瀺されたす


 Item1 Item2 Item3 

私の意芋では、Streamsの新芏参入者にずっお最倧の問題は、最初の芁玠がストリヌムに配眮されるずっず前に、攟出された芁玠の反応を決定でき、この反応の呌び出しをトリガヌできるこずです。


リスニングを終了


䞊蚘のコヌドでは、小さいながらも重芁な郚分が欠萜しおいたす。 listen()はStreamSubscriptionストリヌムサブスクリプションオブゞェクトを返したす。 圌の.cancel()メ゜ッドを呌び出すず、サブスクリプションが終了し、リ゜ヌスが解攟され、リスニング機胜が䞍芁になった埌の呌び出しが防止されたす。


 var controller = new StreamController<String>(); StreamSubscription subscription = controller.stream.listen((item) => print(item)); // This is the Trap controller.add("Item1"); controller.add("Item2"); controller.add("Item3"); //    ,        //  ,     Stream   await Future.delayed(Duration(milliseconds: 500)); subscription.cancel; 

リスナヌの詳现


listen()の関数は、ラムダたたは単玔な関数のいずれかです。


 void myPrint(String message) { print(message); } StreamSubscription subscription = controller.stream.listen((item) => print(item)); //  - StreamSubscription subscription2 = controller.stream.listen(myPrint); //    StreamSubscription subscription3 = controller.stream.listen((item) { print(item); print(item.toUpperCase); }); // - 

重芁な泚意ほずんどのDartストリヌムでは、1回限りのサブスクリプションしか蚱可されおいたせん。぀たり、サブスクリプションの完了埌に再サブスクラむブするこずはできたせん。これは䟋倖をスロヌしたす。 これは、他のRx実装ずの違いです。


listen()の完党な眲名は次のようになりたす。


  /* excerpt from the API doc * The [onError] callback must be of type `void onError(error)` or * `void onError(error, StackTrace stackTrace)`. If [onError] accepts * two arguments it is called with the error object and the stack trace * (which could be `null` if the stream itself received an error without * stack trace). * Otherwise it is called with just the error object. * If [onError] is omitted, any errors on the stream are considered unhandled, * and will be passed to the current [Zone]'s error handler. * By default unhandled async errors are treated * as if they were uncaught top-level errors. * * If this stream closes and sends a done event, the [onDone] handler is * called. If [onDone] is `null`, nothing happens. * * If [cancelOnError] is true, the subscription is automatically canceled * when the first error event is delivered. The default is `false`. */ StreamSubscription<T> listen(void onData(T event), {Function onError, void onDone(), bool cancelOnError}); 

これは、送信されたデヌタに察しお1぀のハンドラヌを枡すだけでなく、耇数のこずができるこずを意味したす。 たた、゚ラヌのハンドラヌず、コントロヌラヌ偎でストリヌムを閉じるハンドラヌ onDone を持぀こずもできたす。 Stream内からonError()れた䟋倖は、 指定した堎合にonError()をonError()たす。 それ以倖の堎合、䟋倖は単に飲み蟌たれ、䜕かがうたくいかなかったこずは決しおわかりたせん。


フラッタヌスレッドの䟋


次の章の理解を容易にするために、別のリポゞトリブランチを䜜成したした。
圌女をクロヌンしおください


最初の䟋ずしお、新しいFlutterプロゞェクトを䜜成するずきに取埗する有名なカりンタヌアプリケヌションを取り䞊げ、少し再線成したした。 アプリケヌションの状態を保存するモデルクラスを远加したした。これは基本的にカりンタヌ倀です。


 class Model { int _counter = 0; StreamController _streamController = new StreamController<int>(); Stream<int> get counterUpdates => _streamController.stream; void incrementCounter() { _counter++; _streamController.add(_counter); } } 

ここでは、非垞に兞型的なテンプレヌトを芋るこずができたす。StreamController党䜓を公開する代わりに、単にStreamプロパティを公開したす。


モデルをUIで䜿甚できるようにするために、InheritedWidgetたたはServiceLocatorを入力したくないため、Appオブゞェクトの静的フィヌルドにしたした。 簡単な䟋では、これでうたくいきたすが、このアプリケヌションでは行いたせん


main.dart远加しmain.dart 。


 class _MyHomePageState extends State<MyHomePage> { int _counter = 0; StreamSubscription streamSubscription; @override void initState() { streamSubscription = MyApp.model.counterUpdates.listen((newVal) => setState(() { _counter = newVal; })); super.initState(); } //   State   ,   , //       @override void dispose() { streamSubscription?.cancel(); super.dispose(); } 

initState()リスナヌを蚭定initState()適した堎所です。ダヌツの良き垂民ずしお、私たちは垞にdispose()でサブスクリプションをリリヌスしたす。


りィゞェットツリヌでは、FABボタンフロヌティングアクションのあるボタンのonPressedハンドラヌを調敎するだけです。


 floatingActionButton: new FloatingActionButton( onPressed: MyApp.model.incrementCounter, tooltip: 'Increment', child: new Icon(Icons.add), ), 

このようにしお、Streamを䜿甚しお、ViewずModelを明確に分離したした。


StreamBuilderを適甚する


出所


必芁に応じおinitState()およびsetState()を䜿甚する代わりに、Flutterには䟿利なStreamBuilderりィゞェットが付属しおいたす。 ご想像のずおり、Stream関数ず、Streamが新しい倀を返すたびに呌び出されるコンストラクタヌメ゜ッドを取りたす。 そしお今、明瀺的な初期化ずリリヌスは必芁ありたせん


 body: new Center( child: new Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ new Text( 'You have pushed the button this many times:', ), StreamBuilder<int>( initialData: 0, stream: MyApp.model.counterUpdates, builder: (context, snappShot) { String valueAsString = 'NoData'; if (snappShot != null && snappShot.hasData) { valueAsString = snappShot.data.toString(); } return Text( valueAsString, style: Theme.of(context).textTheme.display1, ); }), ], ), ), 

ほが完了です、玄束したす。 知っおおくべき3぀のこずを次に瀺したす。



次の投皿では、スレッド内のデヌタを倉換し、その堎でそれを行う方法を芋おいきたす。 蚌拠ず重芁なフィヌドバックを読んでくれたScott Stollに感謝したす。





Source: https://habr.com/ru/post/J450950/


All Articles