RxKotlin 例子不超过15行教程 4----Observer Subscribe 与 Hot/Cold Observable

本节代码中的 observer 就是第二节中的

Observer 与 Subscribe

按照惯例,先来看两段能跑的代码

两段能跑的代码

// 4.1.kt
import io.reactivex.Observable

fun main(args: Array<String>) {
    val observable: Observable<Int> = Observable.range(1, 3)
    observable.subscribe({  // 我知道你要问我为什么 subscribe 后面还可以接三个 Lambda,先看例子,下面说
        //onNext method
        println("Next $it")
    }, {
        //onError Method
        println("Error ${it.message}")
    }, {
        //onComplete Method
        println("All Completed")
    })
}

输出

Next 1
Next 2
Next 3
All Completed

再来一段(上一节用过的例子)

// 3.11.kt
import io.reactivex.Observable

fun main(args: Array<String>) {
    Observable.range(1, 3).subscribe(observer)
}

输出

New Subscription  // 上面那个例子没有这一行
Next 1
Next 2
Next 3
All Completed

上面代码的主旨

Observer

之前(第二节)我们说过,一个 Observer 需要实现四个方法(它们的作用参见第二节)

  • onNext
  • onComplete
  • onError
  • onSubscribe

当我们把 Observable 连接到 Observer 上的时候,系统会调用这四个方法并把相应的值传给它们。

subscribe 的参数都能是什么

subscribe 在 ReactiveX 中有几个重载方法,这里不列出。基本模式有这两个

  • subscribe(onNext,onError,onComplete,onSubscribe)
    这几个参数都可以省略,但是只能从后往前省略(这句是废话)
    是废话也要说,因为 subscribe 是在 Java 文件中定义的,不能使用 Kotlin 的命名参数
    4.1.kt 中省略了 onSubscribe
  • subscribe(observer)
    3.11.kt 已经很清晰,这里不展开了

除了 subscribe 方法,还有 RxKotlin 提供的小语法糖 subscribeBy

这个函数是 RxKotlin 为 Observable (等可以 subscribe 的对象)定义的扩展函数,函数定义如下

fun <T : Any> Observable<T>.subscribeBy(
        onError: (Throwable) -> Unit = onErrorStub,
        onComplete: () -> Unit = onCompleteStub,
        onNext: (T) -> Unit = onNextStub
        ): Disposable = subscribe(onNext, onError, onComplete)  // 好的好的,我知道你要问 Disposable 是什么,稍等。

因为被定义在 Kotlin 文件中,它可以使用命名参数(例子见 第一节 1.kt)

Subscribe

从之前的例子可知,subscribe 可以连接 ObservableObserver
它有两种形式(上面说过,这里再概括一下)

  • onNext 等,以参数的形式传进去
  • 直接传入一个 Observer 对象

如果你选择第一种形式,那么 subscribe 方法是有返回值的,返回值类型是 Disposable (不要急,它的介绍马上就到了)
如果你选择第二种形式,那么 subscribe 方法是有返回值的
这两种形式中的 onSubscribe 都是一个 (d:Disposable):Unit 类型的函数。
那么 Disposable 有什么用呢?

Disposable

disposable: 一次性的,可任意处理的; 用后就抛弃的; 免洗的; 可供使用的。讲真,这几个中文翻译放在这里我觉得并不是很合适,我也没有想到合适的中文翻译(如果有合适的欢迎指出)。我就一直用英文了。
Disposable 对象的 dispose 方法可以停止本次订阅
看一个例子
我保证这个例子是为数不多的长例子之一,真的不能再精简了
下面示例用到了 lateinit 可以自行 Google 下,此处不介绍。(如果有好的链接欢迎发给我,加在这里)

// 4.2.kt
import io.reactivex.Observable
import io.reactivex.Observer
import io.reactivex.disposables.Disposable
import java.util.concurrent.TimeUnit

fun main(args: Array<String>) {

    val observale: Observable<Long> = Observable.interval(100, TimeUnit.MILLISECONDS)

    val observer: Observer<Long> = object : Observer<Long> {
        lateinit var disposable: Disposable

        override fun onSubscribe(d: Disposable) {
            disposable = d
        }

        override fun onNext(item: Long) {
            if (item >= 5 && !disposable.isDisposed) {
                disposable.dispose()
                println("Disposed")
            }
            println("Received $item")
        }

        override fun onError(e: Throwable) {
            println("Error ${e.message}")
        }

        override fun onComplete() {
            println("Complete")
        }

    }

    observale.subscribe(observer)
    Thread.sleep(1000)
}

输出

Received 0
Received 1
Received 2
Received 3
Received 4
Disposed    // 注释1
Received 5 // 注释2
// 注释3

注释1
dispose 处理后不会执行 observer 的 onComplete 方法(所以 Complete 没有输出)
注释2
disposable.dispose() 之后,observer 不会再处理其它值(所以 Received 6 Received 7 等等并没有输出)
但是当前值依然会继续处理(所以 Received 5 依然被输出)
总结一下, Disposable 是用来控制订阅的

下面我们回到 Observable,看看它的分类

Hot/Cold Observable

在本教程前面所有示例中,如果多次订阅同一个 Observable,则所有订阅都会得到从一开始的所有值。
例子

// 4.3.kt
import io.reactivex.Observable
import io.reactivex.rxkotlin.toObservable

// Cold Observables
fun main(args: Array<String>) {
    val observable: Observable<Int> = listOf(1, 2, 3, 4).toObservable()

    observable.subscribe(observer)

    observable.subscribe(observer)
}

输出

New Subscription
Next 1
Next 2
Next 3
Next 4
All Completed
New Subscription
Next 1
Next 2
Next 3
Next 4
All Completed

我们可以看到每一个 Observer 都被推送了了从 1-4 的所有值。
到目前为止,我们遇到的所有 Observable 都是这样的。
这样的 Observable 被称作 Cold Observable
我之前曾经比喻 Observable 为电台,这是有一些不恰当的。因为当你错过时间再打开电台会听不到原先的内容。
Cold Observable 更像是光盘(容量可能无限),随时打开都能从头开始听。

电台这个比喻更适合 Hot Observable,看下一个例子

// 4.4.kt
import io.reactivex.rxkotlin.toObservable

//Hot Observable
fun main(args: Array<String>) {
    val connectableObservable = listOf(1, 2, 3).toObservable().publish()  // 注释1
    connectableObservable.subscribe({ println("Subscription 1: $it") })  // 描点1
    connectableObservable.subscribe({ println("Subscription 2: $it") })  // 描点2
    connectableObservable.connect()  // 注释2
    connectableObservable.subscribe({ println("Subscription 3: $it") })  // 注释3
}

输出

Subscription 1: 1
Subscription 2: 1
Subscription 1: 2
Subscription 2: 2
Subscription 1: 3
Subscription 2: 3
// 并没有输出 Subscription 3

注释1
我们用 publish 方法把 Cold Observable 变成 ConnectableObservable (ConnectableObservableHot Observable 的一种)
注释2
ConnectableObservable描点1描点2 处都不会发送消息,它会在 注释2 处(调用 connect 方法时)开始发送消息
Cold Observable 会在调用 subscribe 时开始发送消息
如果订阅晚了(如 注释3),则会错过一些消息(在这里,注释3 错过了所有消息(计算机速度太快....),接下来有其他例子,不要急)
注释3
订阅3不会收到任何信息

我们来看下一个例子,在这个例子中,调用 connect 方法后我们又增加了新的订阅,这个订阅会丢失部分消息

import io.reactivex.Observable
import java.util.concurrent.TimeUnit

fun main(args: Array<String>) {
    val connectableObservable = Observable.interval(10, TimeUnit.MILLISECONDS).publish()
    connectableObservable.subscribe({ println("Subscription 1: $it") })
    connectableObservable.subscribe({ println("Subscription 2: $it") })
    connectableObservable.connect()  // ConnectableObservable 开始发送消息
    println("Sleep 1 starts")
    Thread.sleep(20)
    println("Sleep 1 ends")
    connectableObservable.subscribe({ println("Subscription 3: $it") })  // 不用再次调用 connect 方法
    println("Sleep 2 starts")
    Thread.sleep(30)
    println("Sleep 2 ends")
}

输出(有点长)

Sleep 1 starts
Subscription 1: 0
Subscription 2: 0
Subscription 1: 1
Subscription 2: 1 // 注释1
Sleep 1 ends      // 开始 订阅3
Sleep 2 starts
Subscription 1: 2
Subscription 2: 2
Subscription 3: 2  // 注释2
Subscription 1: 3
Subscription 2: 3
Subscription 3: 3
Subscription 1: 4
Subscription 2: 4
Subscription 3: 4
Sleep 2 ends

注释1
到这里我们没有开始 订阅3 所以没有输出任何 Subscription 3
注释2
订阅3 的输出是从 2 开始的,它错过了 01

这一节到这里就 OK 了,明天说 Subject

RxKotlin 例子不超过15行教程 1----环境配置与初体验

RxKotlin 例子不超过15行教程 2----Observable Observer 与 Subscribe 简介

RxKotlin 例子不超过15行教程 3----Observable 的创建

RxKotlin 例子不超过15行教程 4----Observer Subscribe 与 Hot/Cold Observable

RxKotlin 例子不超过15行教程 5----Subject

RxKotlin 例子不超过15行教程 6----Operator 与 Marble Diagram

RxKotlin 例子不超过15行教程 7----Backpressure Flowable 与 Subscriber 简介

RxKotlin 例子不超过15行教程 8----Error Handling

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