RxDart学习笔记

参考了以下文章,感谢大侠们的无私付出:
https://www.jianshu.com/p/612d43c8915f
https://juejin.im/post/5bcea438e51d4536c65d2232
https://www.jianshu.com/p/00060710a890

  • Subject实现并扩展了StreamController,它符合StreamController的所有规范。假如您之前使用的StreamController,那么你可以直接替换为Subject。你可以把它想像成streamController。

  • Observable实现并扩展了Stream。它将常用的stream和streamTransformer组合成了非常好用的api。你可以把它想像成stream。

创建Observables

  • 从一个Stream中创建:
var controller = new StreamController<String>();
var streamObservable = new Observable(controller.stream);
streamObservable.listen(print);
  • 通过Future创建:fromFuture
var obs = Observable.fromFuture(new Future.value("Hello"));
obs.listen(print); 
  • 通过Iterable创建:fromIterable
var obs = Observable.fromInterable([1,2,3,4,5]);
obs.listen(print);
  • 从单个值中创建
var justObservable = Observable<int>.just(42);
justObservable.listen(print);
  • 其他骚操作
    基本与RxJava一致,只是subscribe方法换成了listen,取消变成了StreamSubscription 。
  • 从Future中创建
    通过一个Future创建的Observable,会先等待Future执行完毕,完后发射数据,这个输出数据就是Future的执行结果,如果Future没有任何返回值,那么输出null。另一种从Future中创建Stream的方法是调用Future的toStream()方法。
 Future<String> asyncFunction() async {
    return Future.delayed(const Duration(seconds: 1), 
    							() => "AsyncRsult");
  }

  test('Create Observable from Future', () async {
    print('start');
    var fromFutureObservable = Observable.fromFuture(asyncFunction());
    fromFutureObservable.listen(print);
  }

Subjects

Subjects是RxDart的流控制器(StreamController),但Subjects但行为跟StreamControllers还是有些区别的:

  • 你可以在一个Subject上直接listen(),而不需要拥有对这个Stream的访问权限;
  • 你可以添加多个subscription,它们会同时收到同样的数据;

Subjects有三种类型:
PublishSubjects
  和StreamControllers的行为很像,也支持多个监听,默认是sync是false,也就是说里面是一个AsyncBroadcastStreamController 异步广播流

var subject = new PublishSubject<String>();
  subject.listen((item) => print(item));
  subject.add("Item1");

  // 添加第二个listener
  subject.listen((item) => print(item.toUpperCase()));

  subject.add("Item2");
  subject.add("Item3");

  subject.close();

输出结果:
在这里插入图片描述
因为第二个监听是在中途加进来的,所以它并没有监听到数据Item1。

BehaviourSubject
  每一个新加的监听,接收到的第一个数据都是上一个数据(再往前的数据不会监听到,只会缓存一个数据)。

 var subject = new BehaviorSubject<String>();
  subject.listen((item) => print(item));

  subject.add("Item1");
  subject.add("Item2");

  subject.listen((item) => print(item.toUpperCase()));

  subject.add("Item3");
  subject.close();

在这里插入图片描述
我们发现第二个subscriber没有监听到Item1,但是监听到了Item2,**而且第二个subscriber比第一个subscriber先监听到了Item3。**这是因为,你没法决定多个监听的服务顺序(实际上对于单个item,总是后加的监听先接收到数据),但是每个监听获取到的数据依然是有序的。BehaviourSubject只会为后加的Subscribers缓存最近的一条输出数据。
总结:每一个新加的监听接收到的第一条数据,是最近的那条数据(也就是只会缓存最近一条数据的意思);对于单条数据而言,总是后加的监听先接收到。
也可以添加默认接收数据的值seeded:
在这里插入图片描述
在这里插入图片描述
如果你想要缓存更多的数据,可以使用ReplaySubject,但是大多数情况下我们都用不到。
ReplaySubject
回放已经消失的事件。

final subject1 = ReplaySubject<int>();

subject1.add(1);
subject1.add(2);
subject1.add(3);

subject1.stream.listen(print); // prints 1, 2, 3
subject1.stream.listen(print); // prints 1, 2, 3
subject1.stream.listen(print); // prints 1, 2, 3

final subject2 = ReplaySubject<int>(maxSize: 2);

subject2.add(1);
subject2.add(2);
subject2.add(3);

subject2.stream.listen(print); // prints 2, 3
subject2.stream.listen(print); // prints 2, 3
subject2.stream.listen(print); // prints 2, 3

常用的数据变换

map

与RxJava一样,不再赘述。
除了Streams之外,每一个Iterable都提供了map方法,将其转化为一个List。
创建周期性事件

periodic重复性操作

var timerObservable = Observable.periodic(Duration(seconds: 1), 
		(x) => x.toString() );//函数可以不写,x是事件的序列号,从0开始
timerObservable.listen(print);

完整的代码如下:

 StreamSubscription subs;
  subs = Observable.periodic(Duration(seconds: 10), (index) {
    return "primitive $index";
  }).map((value) => "ss $value").listen((data) {
    print("data is $data");
    if (data.contains("8")) {
      subs.cancel();
    }
  });

interval实现上面的逻辑

var obs = Observable(Stream.fromIterable([1,2,3,4,5]))
    .interval(new Duration(seconds: 1));

obs.listen(print);

输出:1 … 2 … 3 … 4 … 5,其中…代表停顿了一秒。

延时操作

 Observable.timer("Hello Timer", Duration(seconds: 20)).listen((data) {
    print("data is $data");
  });

concat

与RxJava一致,不再赘述。

Where:数据过滤

如果你只关心Stream中的特定数据,那么可以使用.where()函数。这个函数其实就是替代if语句的,但是.where()会更加方便阅读:

var subject = new PublishSubject<int>();

  subject.where((val) => val.isOdd)
  		.listen((val) => print('This only prints odd numbers: $val'));

  subject.where((val) => val.isEven)
  		.listen((val) => print('This only prints even numbers: $val'));

  subject.add(1);
  subject.add(2);
  subject.add(3);
  subject.close();

在这里插入图片描述

Debounce:数据拦截

与RxJava一致,不再赘述。

Expand:展开数组

var list = [1, 8, 9, 20, 36, 68, 99];
  var subject = Observable.fromIterable(list);
  subject.expand((data) {
	 //对应也可以是[data,"number is $data"]表示添加元素
    return ["number is $data"];
  }).listen((data) {
    print(data);
  });

在这里插入图片描述
乍一看和map差不多,但它更加强大,可以添加元素,修改元素。

mergeWith

即RxJava的merge。

Distinct:过滤相同数据

同RxJava,不再赘述。

ZipWith:数据合并

zipWith也是将一个Stream和另一个合并到一起,但是它和.mergeWith不一样。.mergeWith只要拿到了数据就立刻发射出去,但是zipWith会等到所有数据都接收完毕后,将这些数据重组后再发射出去。与RxJava一致,不再赘述。

CombineLatest:组合数据

combineLatest跟merge和zip一样也是组合数据,但是有一些轻微都区别。它接收到一个Stream的数据后,不仅仅会发射这个Stream带来的数据,还会将其他Stream中的最近的数据也发射出去。也就是说,有n个Stream,它每一次就发射n个数据,发射的数据是每个Stream上最近的一条数据;任意一个Stream的数据进来的时候,都会触发一次发射;刚开始的时候,数据种类不足n时,会等待(也就是第一次发射必须保证每个Stream都有数据被传递过来),与RxJava一致。

检查每一个item:every

every会检查每个item是否符合要求,然后它将会返回一个能够被转化为 Observable 的 AsObservableFuture。

var obs = Observable.fromIterable([1,2,3,4,5]);
obs.every((x)=> x < 10).asObservable().listen(print);

输出结果:true

AsyncMap:异步数据转换

除了map(),还有一个asyncMap方法,让你可以在map函数中进行异步操作。

Observable<CombinedMessage> getDependendMessages() {

  Observable<NewsMessage> news = newsCollection.snapshots().expand((snapShot) {
    return snapShot.documents;
  }).map<NewsMessage>((document) {
    return NewsMessage.fromMap(document.data);
  });

  return news.asyncMap((newsEntry) async {
    var weatherDocuments =
        await weatherCollection.where('location', isEqualTo: newsEntry.location).getDocuments();
    return new CombinedMessage(
        WeatherForecast.fromMap(weatherDocuments.documents.first.data), newsEntry);
  });
}

类似RxJava的切换线程。

需要注意的地方

RxDart的transforming函数应该只用来处理数据流,所以不要尝试在这些函数中修改变量/状态(variables/state),这些代码请写在.listen中。所以,不要这么写:

Observable.fromFuture(getProduct())
        .map<Product>((jsonString) { 
     var product = Product.fromJson(jsonString);
    database.save(product);
    setState((){ _product =  product });
    return product;
}).listen();

而是这么写:

Observable.fromFuture(getProduct())
        .map<Product>((jsonString) => Product.fromJson(jsonString))
        .listen( (product) {
          database.save(product);  
          setState((){ _product =  product });
        });

map()函数的唯一作用就是数据转换,不要在里面做任何多余的操作。在map函数里面写其他操作也会降低代码的可读性,也容易隐藏一些bug。

tips

  • 为了防止内存泄漏,请在适当的时候调用subscriptions的cancel()方法,或者者dispose掉你的StreamControllers,或者关闭你的Subjects。

  • Dart中 Observables 默认是单一订阅。如果您尝试两次收听它,则会抛出 StateError 。你可以使用工厂方法或者 asBroadcastStream 将其转化为多订阅流。

   var obs = Observable(Stream.fromIterable([1,2,3,4,5])).asBroadcastStream();
  • 很多方法的返回值并不是一个 Single 也不是一个 Observable 而是必须返回一个Dart的 Future。幸运的是你很容易找到一些方法,把他们转化成回 stream。

  • 出现错误时,Dart中的Stream不会默认关闭。但是在Rxdart中,Error会导致Observable终止,除非它被运算符拦截。

  • 默认情况下Dart中Stream是异步的,而Observables默认是同步的。

  • 在处理多订阅Observable的时候,onListen方法只有在第一次会被调用。且各个订阅者之间不会互相干涉。

 var obs = Observable(Stream.fromIterable([1,2,3,4,5])).asBroadcastStream();

  //第一个订阅者
  obs.interval(Duration(seconds: 1)).map((item) => ++item).listen(print);
  //第二个订阅者
  obs.listen(print);

输出:1 2 3 4 5 2 3 4 5 6

应用

生成重复的view

  var subject = ReplaySubject<List<String>>();
    subject.add(['a', 'b', 'c', 'd']);
    return Scaffold(
      appBar: AppBar(
        title: Text("这是根据StreamBuilder生成的view tree"),
      ),
      body: Center(
        child: StreamBuilder(
            stream: subject.stream,
            builder: (BuildContext context, AsyncSnapshot<List<String>> snapshot) {
              subject.close();
              return Column(
                children: snapshot.data.map((text) {
                  return Text(
                    text,
                    style: TextStyle(fontSize: 30),
                  );
                }).toList(),
              );
            }),
      ),
    );
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值