默认情况,我们使用Observable来监听时,有以下几个问题.
先来看个例子:
- 模拟请求接口查询数据
// 模拟查询数据
func dealwithData(inputText:String)-> Observable<Any>{
print("请求网络了 \(Thread.current)") // data
return Observable<Any>.create({ (ob) -> Disposable in
if inputText == "1234" {
ob.onError(NSError.init(domain: "com.lgcooci.cn", code: 10086, userInfo: nil))
}
DispatchQueue.global().async {
print("发送之前看看: \(Thread.current)")
ob.onNext("已经输入:\(inputText)")
ob.onCompleted()
}
return Disposables.create()
})
}
- 使用ob序列监听
let result = inputTF.rx.text.skip(1).flatMap { [weak self](input) -> Observable<Any> in
return (self?.dealwithData(inputText: input ?? ""))!
}
- 接下来我们连续订阅两次
//第一次订阅
result.subscribe { (even) in
print(even)
print(Thread.current)
}.disposed(by: disposeBag)
//第二次订阅
result.subscribe { (even) in
print(even)
print(Thread.current)
}.disposed(by: disposeBag)
提前准备好的界面如下:
-
运行 ,输入框输入1 打印结果如下
打印结果中看出, 订阅两次时,我们会请求两次网络.
请求多次网络解决办法
添加观察时增加共享指定:
let result = inputTF.rx.text.skip(1).flatMap { [weak self](input) -> Observable<Any> in
return (self?.dealwithData(inputText: input ?? ""))!
}.share(replay: 1, scope: .whileConnected)
再运行:
看到订阅两次但是只执行一次监听方法.但是我们仍然看到接受到事件时是处于子线程, 意味着不能更新UI事件.继续解决
响应异步解决办法 : 指定序列监听执行线程
let result = inputTF.rx.text.skip(1).flatMap { [weak self](input) -> Observable<Any> in
return (self?.dealwithData(inputText: input ?? ""))!
.observeOn(MainScheduler.instance)
}.share(replay: 1, scope: .whileConnected)
打印结果:
我们看到当输入内容为1234时,我们调用了
ob.onError(NSError.init(domain: "com.lgcooci.cn", code: 10086, userInfo: nil))
可是输入1234时并没有监听到onError事件.
增加错误handle
let result = inputTF.rx.text.skip(1).flatMap { [weak self](input) -> Observable<Any> in
return (self?.dealwithData(inputText: input ?? ""))!
.observeOn(MainScheduler.instance)
.catchErrorJustReturn("检测到了错误事件")
}.share(replay: 1, scope: .whileConnected)
然后我们再输入1234
总结:
我们为了处理 请求多次网络解决办法,响应异步解决办法 , 增加错误handle , 需要分别额外增加三步处理, 可是在实际项目中,往往不会使用这种方式. 而是用Driver来处理.
Driver(特征序列)
1 - 基本介绍
(1)Driver 可以说是最复杂的 trait,它的目标是提供一种简便的方式在 UI 层编写响应式代码。
(2)如果我们的序列满足如下特征,就可以使用它:
- 不会产生 error 事件
- 一定在主线程监听(MainScheduler)
- 共享状态变化(shareReplayLatestWhileConnected)
2 - 为什么要使用 Driver?
(1)Driver 最常使用的场景应该就是需要用序列来驱动应用程序的情况了,比如:
通过 CoreData 模型驱动 UI
使用一个 UI 元素值(绑定)来驱动另一个 UI 元素值
(2)与普通的操作系统驱动程序一样,如果出现序列错误,应用程序将停止响应用户输入。
(3)在主线程上观察到这些元素也是极其重要的,因为 UI 元素和应用程序逻辑通常不是线程安全的。
(4)此外,使用构建 Driver 的可观察的序列,它是共享状态变化。
3 - 使用案例
let result = inputTF.rx.text.orEmpty
.asDriver()
.flatMap { return self.dealwithData(inputText: $0)
.asDriver(onErrorJustReturn: "检测到了错误事件") }
第一次订阅
//第一次订阅,并把内容绑定到另一个UILabel上
result.map{ "长度:\( ($0 as! String).count )" }
.drive(self.textLabel.rx.text)
.disposed(by: disposeBag)
第二次订阅
//第二次订阅,并把内容绑定到另一个UIButton上
result.map { "\($0 as! String)"}
.drive(self.btn.rx.title())
.disposed(by: disposeBag)
执行结果:
总结:
Driver特征是序列实际在开发中专门用于
- 不会产生 error 事件
- 一定在主线程监听(MainScheduler)
- 共享状态变化(shareReplayLatestWhileConnected)
由于 drive 方法只能被 Driver 调用。这意味着,如果代码存在 drive,那么这个序列不会产生错误事件并且一定在主线程监听。这样我们就可以安全的绑定 UI元素。