1 什么是Stream?
Stream是Dart用來處理異步的API,和同樣用來處理異步的Future不同的是,Stream可以異步的回傳多個結果,而Future只能回傳一個,如果你對Future有疑問,可以參考作者的上一篇文章,Dart基礎——Dart異步Future與事件回圈Event Loop,
2 如何創建Stream?
1.1使用Stream的構造方法
Stream periodicStream = Stream.periodic(Duration(seconds: 2), (num) {
return num;
});
periodic構造方法主要有兩個引數,第一個引數型別為Duration(時間間隔),第二個引數型別為Function,Function每隔一個Duration(時間間隔)會被呼叫一次,引數num為事件呼叫的次數,從0開始依次遞增,
翻閱原始碼 Stream.periodic是使用Timer.periodic加_SyncStreamController實作的
1.2將方法的回傳值宣告為Stream
Stream<String> timedCounter(Duration interval, [int maxCount]) async* {
int i = 0;
while (true) {
//延遲interval(時間間隔)執行一次
await Future.delayed(interval);
//回傳i i++
yield "stream回傳${i++}";
if (i == maxCount) break;
}
}
看到這里你可能會有一些疑問什么是async*和yield?
yield為一個用async *修飾回傳值為Stream的函式回傳一個值,它就像return,不過他不會結束函式
Stream asynchronousNaturalsTo(n) async* {
int k = 0;
while (k < n) yield k++;
}
這里涉及到了Dart的生成器函式概念,在這里你只需要簡單理解yield的作用就可以了
1.3使用StreamController
var _controller = StreamController<int>();
var _count = 1;
createStream() {
//函式每隔一秒呼叫一次
Timer.periodic(Duration(seconds: 1), (t) {
_controller.sink.add(_count);
_count++;
});
}
我們主要使用_controller的兩個屬性,使用_controller.Stream獲取流,使用_controller.sink.add向流中添加資料,上面的例子使用定時器,每隔一秒向流中添加資料_count,
3 Stream的常用方法
接下來介紹一下Stream的常用方法
PS:以下Stream常用方法的展示都是用下面代碼創建的流
Stream periodicStream = Stream.periodic(Duration(seconds: 1), (num) {
return num;
});
3.1 listen
listen作為使用Stream最重要的方法,主要用于監聽流的資料變化,每當流的資料變化時,listen中的方法都會被呼叫,
periodicStream.listen((event) {
print(event);
});
listen方法默認引數為Function,引數中的event為上面示例中回傳的num,每當流回傳新的資料時,listen方法都會被呼叫,
控制臺輸出如下
0
1
2
3
4
列印流回傳的資料
listen的onError引數當流出現錯誤時呼叫,
listen的onDone引數當流關閉時呼叫,
還有一個cancelOnError屬性,默認情況下為true,可以將其設定為false以使訂閱在發生錯誤后也能繼續進行,
3.2 map
Stream.periodic(Duration(seconds: 1), (num) {
return num;
}).map((num) => num * 2)
使用map將流回傳的資料進行轉換
控制臺輸出如下
0
2
4
6
3.3 asBroadcastStream()&broadcast
通過Stream的asBroadcastStream()或StreamController的broadcast將單訂閱的流轉換為多訂閱流
什么是單訂閱流和多訂閱流?
3.3.1 單訂閱流
單訂閱流顧名思義,此流只能有一個訂閱者,也就是單訂閱流的listen方法只能被呼叫一次,當第二次呼叫單訂閱流的listen時會報錯,值得一提的是,當我們創建流時,默認創建的就是單訂閱流,
3.3.2 多訂閱流
顧名思義,此流可以有多個訂閱者,也就是多訂閱流的listen方法可以被多次呼叫,通過Stream的asBroadcastStream()或StreamController的broadcast將單訂閱流轉換為多訂閱流,
創建多訂閱流
Stream broadcastStream = Stream.periodic(Duration(seconds: 5), (num) {
return num;
}).asBroadcastStream();
var _controller = StreamController<int>.broadcast()
3.3.3 單訂閱流與多訂閱流的區別
第一個區別
第一個區別就是上面提到的訂閱者數量的區別
第二個區別
我們重點要談論一下兩種流的第二個區別
第二個區別就是單訂閱流會持有自己的資料,當訂閱者出現時將自身持有的資料全部回傳給訂閱者,而多訂閱流不會持有任何資料,如果多訂閱流沒有訂閱者,多訂閱流會把資料丟棄,
下面我們用兩端代碼來展示兩種流處理資料上的差別
單訂閱流代碼展示
創建流
var _controller = StreamController<int>.broadcast();
var _count = 1;
createStream() {
Timer.periodic(Duration(seconds: 1), (t) {
_controller.sink.add(_count);
_count++;
});
}
訂閱流
createStream();
Future.delayed(Duration(seconds: 5), () {
_controller.stream.listen((event) {
print("單訂閱流$event");
});
});
控制臺輸出如下

可以看到,單訂閱流即使前五秒我們沒有訂閱,但單訂閱流還是在持有資料,當訂閱者出現時將持有的所有資料發送給訂閱者,
多訂閱流代碼展示
創建流
var _controller = StreamController<int>.broadcast();
var _count = 1;
createStream() {
Timer.periodic(Duration(seconds: 1), (t) {
_controller.sink.add(_count);
_count++;
});
}
訂閱流
createStream();
Future.delayed(Duration(seconds: 5), () {
_controller.stream.listen((event) {
print("多訂閱流$event");
});
});
Future.delayed(Duration(seconds: 10), () {
_controller.stream.listen((event) {
print("多訂閱流二$event");
});
});
控制臺輸出

可以看到多訂閱流產生的前五條資料都被丟棄了,只有訂閱者出現后生成的資料被發送給了訂閱者,
代碼看完想必你已經理解了單訂閱流與多訂閱流的第二種區別,我制作了兩種流程圖幫助你理解

3.4 其他方法
處理 Stream 的方法
下面這些 Stream 類中的方法可以對 Stream 進行處理并回傳結果:
Future<T> get first;
Future<bool> get isEmpty;
Future<T> get last;
Future<int> get length;
Future<T> get single;
Future<bool> any(bool Function(T element) test);
Future<bool> contains(Object needle);
Future<E> drain<E>([E futureValue]);
Future<T> elementAt(int index);
Future<bool> every(bool Function(T element) test);
Future<T> firstWhere(bool Function(T element) test, {T Function() orElse});
Future<S> fold<S>(S initialValue, S Function(S previous, T element) combine);
Future forEach(void Function(T element) action);
Future<String> join([String separator = ""]);
Future<T> lastWhere(bool Function(T element) test, {T Function() orElse});
Future pipe(StreamConsumer<T> streamConsumer);
Future<T> reduce(T Function(T previous, T element) combine);
Future<T> singleWhere(bool Function(T element) test, {T Function() orElse});
Future<List<T>> toList();
Future<Set<T>> toSet();
4 管理流訂閱
我們可以使用StreamSubscription物件來對流的訂閱進行管理,listen方法的回傳值就是StreamSubscription物件
StreamSubscription subscription =
Stream.periodic(Duration(seconds: 1), (num) {
return num;
}).listen((num) {
print(num);
});
4.1 暫停訂閱
subscription.pause();
4.2 恢復訂閱
subscription.resume();
4.3 取消訂閱
subscription2.cancel();
當不需要監聽流時記得呼叫這個方法,否則會造成記憶體泄漏
4.4 操作流訂閱的例子
以下示例用來展示如何操作流訂閱
創建流
static var _controller = StreamController<int>();
var _count = 1;
createStream() {
Timer.periodic(Duration(seconds: 1), (t) {
_controller.sink.add(_count);
_count++;
});
}
創建監聽及監聽管理物件
StreamSubscription subscription2 = _controller.stream.listen((event) {
print("單訂閱流$event");
});
操作流訂閱的方法
createStream();
Future.delayed(Duration(seconds: 3), () {
print("暫停");
subscription2.pause();
});
Future.delayed(Duration(seconds: 5), () {
print("繼續");
subscription2.resume();
});
Future.delayed(Duration(seconds: 7), () {
print("取消");
subscription2.cancel();
});
輸出如下
5 在Flutter中使用StreamBuilder組件
5.1 StreamBuilder組件介紹
StreamBuilder組件主要有兩個引數
第一個引數stream,要訂閱的流
第二個引數builder,widget構建函式
可以使用builder函式的snapshot.connectionState屬性根據流的不同狀態回傳不同的組件
每當StreamBuilder監聽的stream有資料變化時,builder函式就會被呼叫,組件重新構建,
5.2示例代碼
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_demo/util/util.dart';
/// Copyright (C), 2020-2020, flutter_demo
/// FileName: streamBuilder_demo
/// Author: Jack
/// Date: 2020/12/27
/// Description:
class StreamBuilderDemo extends StatelessWidget {
//創建流
Stream<int> _stream() {
Duration interval = Duration(seconds: 1);
Stream<int> stream = Stream<int>.periodic(interval, (num) {
return num;
});
stream = stream.take(59);
return stream;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Stream Demo'),
),
body: Center(
child: StreamBuilder(
stream: _stream(),
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Text(
'1 Minute Completed',
style: TextStyle(
fontSize: 30.0,
),
);
} else if (snapshot.connectionState == ConnectionState.waiting) {
return Text(
'Waiting For Stream',
style: TextStyle(
fontSize: 30.0,
),
);
}
return Text(
'00:${snapshot.data.toString().padLeft(2, '0')}',
style: TextStyle(
fontSize: 30.0,
),
);
},
),
),
);
}
}

6 完整示例
上文所有的代碼示例都在作者的GiuHub上,https://github.com/jack0-0wu/flutter_demo,里面還包含了一些常用flutter功能的展示,

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/241829.html
標籤:Android
上一篇:關于QOpenGLContext
