flutter问题深入研究什么Stream

StreamFuture都是Dart:async库的核心API,对异步提供了非常好的支持。

我思考了很久,究竟应该如何向大家介绍Stream(流)。因为Stream非常有用,它是为处理异步事件而生的。而在应用中有大量的场景需要使用异步事件,例如请求网络,和用户交互等等,它们都无法同步完成。Stream能够极大的帮助我们处理这些问题。😍

但是对于刚接触的新手来说,流确实足够抽象,以至于大家需要花费非常多的时间来理解它。

所以我将会尽我所能向大家介绍Stream。

🏊🏻‍Stream

🤔什么是Stream

Stream非常有特点但却不太好理解,我与其按照字面意思把它看作流,更愿意把它看成一个工厂或者是机器。

<figcaption></figcaption>

我们来看看这个机器它有什么特点:

  • 它有一个入口,可以放东西/指令(anything)
  • 这个机器不知道入口什么时候会放东西进来
  • 中间的机器能够生产或者加工,这应该会耗费一些时间
  • 他有一个出口,应该会有产品从那出来
  • 我们也不知道到底什么时候产品会从出口出来

整个过程,时间都是一个不确定因素,我们随时都可以向这个机器的入口放东西进去,放进去了以后机器进行处理,但是我们并不知道它多久处理完。所以出口是需要专门派人盯着的,等待机器流出东西来。整个过程都是以异步的眼光来看的。

✈️我们将机器模型转化成Stream

[图片上传失败...(image-c01deb-1561013840446)]

<figcaption></figcaption>

  • 这个大机器就是StreamController,它是创建流的方式之一。
  • StreamController有一个入口,叫做sink
  • sink可以使用add方法放东西进来,放进去以后就不再关心了。
  • 当有东西从sink进来以后,我们的机器就开始工作啦,空空空。
  • StreamController有一个出口,叫做stream
  • 机器处理完毕后就会把产品从出口丢出来,但是我们并不知道什么时候会出来,所以我们需要使用listen方法一直监听这个出口。
  • 而且当多个物品被放进来了之后,它不会打乱顺序,而是先入先出。

通过这个例子,相信大家对流应该都有了基础印象,那么要解释后面的东西就不难了。

🤔如何使用Stream

获得Stream的方法:

  • 通过构造函数
  • 使用StreamController
  • IO Stream

stream有三个构造方法:

  • Stream.fromFuture:从Future创建新的单订阅流,当future完成时将触发一个data或者error,然后使用Down事件关闭这个流。

  • Stream.fromFutures:从一组Future创建一个单订阅流,每个future都有自己的data或者error事件,当整个Futures完成后,流将会关闭。如果Futures为空,流将会立刻关闭。

  • Stream.fromIterable:创建从一个集合中获取其数据的单订阅流。


Stream.fromIntreable([1,2,3]);
复制代码

🧐监听Stream的方法

监听一个流最常见的方法就是listen。当有事件发出时,流将会通知listener。Listen方法提供了这几种触发事件:

  • onData(必填):收到数据时触发
  • onError:收到Error时触发
  • onDone:结束时触发
  • unsubscribeOnError:遇到第一个Error时是否取消订阅,默认为false

😏 StreamController

如果你想创建一条新的流的话,非常简单!😀 使用StreamController,它为你提供了非常丰富的功能,你能够在streamController上发送数据,处理错误,并获得结果!

//任意类型的流
StreamController controller = StreamController();
controller.sink.add(123);
controller.sink.add("xyz");
controller.sink.add(Anything);

//创建一条处理int类型的流
StreamController<int> numController = StreamController();
numController.sink.add(123);
复制代码

泛型定义了我们能向流上推送什么类型的数据。它可以是任何类型!

我们再来看看如何获取最后的结果。

StreamController controller = StreamController();

//监听这个流的出口,当有data流出时,打印这个data
StreamSubscription subscription =
controller.stream.listen((data)=>print("$data"));

controller.sink.add(123);
复制代码

输出: 123

你需要将一个方法交给stream的listen函数,这个方法入参(data)是我们的StreamController处理完毕后产生的结果,我们监听出口,并获得了这个结果(data)。这里可以使用lambda表达式,也可以是其他任何函数。

(这里我为了方便区分,把listen说成函数,(data)=>print(data)说成方法,其实是一个东西。)

🤠Transforming an existing stream

假如你已经有了一个流,你可以通过它转化成为一条新的流。非常简单!流提供了map(),where(),expand(),和take()方法,能够轻松将已有的流转化为新的流。

where

如果你想要筛选掉一些不想要的事件。例如一个猜数游戏,用户可以输入数字,当输入正确的时候,我们做出一定反应。而我们必须筛选掉所有错误的答案,这个时候我们可以使用where筛选掉不需要的数字。

stream.where((event){...})
复制代码

where函数接收一个事件,每当这个流有东西流到where函数的时候,这就是那个事件。我们或许根本不需要这个事件,但是必须作为参数传入。

take

如果你想要控制这个流最多能传多少个东西。比如输入密码,我们可能想让用户最多输四次,那么我们可以使用take来限制。

stream.take(4);
复制代码

take函数接收一个int,代表最多能经过take函数的事件次数。当传输次数达到这个数字时,这个流将会关闭,无法再传输。

transform

如果你需要更多的控制转换,那么请使用transform()方法。他需要配合StreamTransformer进行使用。我们先来看下面一段猜数游戏,然后我会向你解释。

StreamController<int> controller = StreamController<int>();

final transformer = StreamTransformer<int,String>.fromHandlers(
    handleData:(value, sink){
    if(value==100){
      sink.add("你猜对了");
    }
    else{ sink.addError('还没猜中,再试一次吧');
    }
  });

  controller.stream
            .transform(transformer)
            .listen(
                (data) => print(data),
                onError:(err) => print(err));

    controller.sink.add(23);
    //controller.sink.add(100);
复制代码

输出: 还没猜中,再试一次吧

StreamTransformer<S,T>是我们stream的检查员,他负责接收stream通过的信息,然后进行处理返回一条新的流。

  • S代表之前的流的输入类型,我们这里是输入一个数字,所以是int。
  • T代表转化后流的输入类型,我们这里add进去的是一串字符串,所以是String。
  • handleData接收一个value并创建一条新的流并暴露sink,我们可以在这里对流进行转化。
  • 我们还可以addError进去告诉后面有问题。

然后我们监听transform之后的流,当转换好的event流出时,我们打印这个event,这个event就是我们刚才add进sink的数据。onError能够捕捉到我们add进去的err。

🤨Stream的种类

流有两种

  • "Single-subscription" streams 单订阅流
  • "broadcast" streams 多订阅流

"Single-subscription" streams

单个订阅流在流的整个生命周期内仅允许有一个listener。它在有收听者之前不会生成事件,并且在取消收听时它会停止发送事件,即使你仍然在Sink.add更多事件。

即使在第一个订阅被取消后,也不允许在单个订阅流上进行两次侦听。

单订阅流通常用于流式传输更大的连续数据块,如文件I / O.

StreamController controller = StreamController();

controller.stream.listen((data)=> print(data));
controller.stream.listen((data)=> print(data));

controller.sink.add(123);
复制代码

输出: Bad state: Stream has already been listened to. 单订阅流不能有多个收听者。

"Broadcast" streams

广播流允许任意数量的收听者,且无论是否有收听者,他都能产生事件。所以中途进来的收听者将不会收到之前的消息。

如果多个收听者想要收听单个订阅流,请使用asBroadcastStream在非广播流之上创建广播流。

如果在触发事件时将收听者添加到广播流,则该侦听器将不会接收当前正在触发的事件。如果取消收听,收听者会立即停止接收事件。

一般的流都是单订阅流。从Stream继承的广播流必须重写isBroadcast 才能返回true。

StreamController controller = StreamController();
//将单订阅流转化为广播流
Stream stream = controller.stream.asBroadcastStream();

stream.listen((data)=> print(data));
stream.listen((data)=> print(data));

controller.sink.add(123);
复制代码

输出: 123 123

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容