RxSwift学习——Schedulers (调度器)

这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战

前言

其实有关调度器这个一节,我原本计划是不打算单独写文章做介绍的。

原因有二:

  • 通过Rx框架,大部分时候你完全不用考虑使用调度器,按照流水线的思路去写即可,其他的让Rx给你去调度。

  • 涉及异步操作的网络请求,其实已经有RxMoya了,它已经被封装的特别好了,省去了需要费心的地方。

但是当我看着调度器方法命名的时候,我确实觉得,这实在太太太容易造成误解了。所以还是来说说吧。

调度器

我们先上一个例子,接口请求,耗时操作,在全队队列去操作,完成后回主队列赋值数据。我们用GCD来实现一下:

/// 后台取得数据
DispatchQueue.global(qos: .userInitiated).async {
    let data = try? Data(contentsOf: URL(string: "https://www.wanandroid.com/banner/json")!
    
    /// 主线程处理结果
    DispatchQueue.main.async {
        let string = String(data: data, encoding: .utf8)
        print(string)
    }
}
复制代码

思考一下,我们如何用一个序列来完成实现上线的情况,展示不考虑线程:

  • 由于是网络请求,可以考虑使用序列的Single类型。

  • 使用Single的工厂方法生成一个Single<Data>的序列。

  • 这个Single<Data>使用subscribe方法中去实现处理结果。

为了便于理解,我们拆成两端代码:

/// 生成一个序列
let single = Single<Data>.create { single in
    print("序列生成的线程")
    print(Thread.current)

    if let data = try? Data(contentsOf: URL(string: "https://www.wanandroid.com/banner/json")!) {
        single(.success(data))
    }else {
        single(.error(SomeError()))
    }
    return Disposables.create()
 }
 
 /// 序列的处理结果
single.subscribe { data in
    print("subscribe处理的线程")
    print(Thread.current)
    let string = String(data: data, encoding: .utf8)
    print(string)
} onError: { _ in

}.disposed(by: disposeBag)
复制代码

在这里,我在生成序列的时候(Single<Data>.create)打印了当前的线程,同时在订阅的时候(subscribe)也打印了当前线程,我们来看看日志:

序列生成的线程
<NSThread: 0x2839b0e00>{number = 1, name = main}

subscribe处理的线程
<NSThread: 0x2839b0e00>{number = 1, name = main}
复制代码

可以看的处理,默认序列生成与数据处理都是在主线程中进行的。

这不符合我们的预期,我们希望生成序列的时候在一个非主线程中完成,怎么操作呢?

/// 生成一个序列
let single = Single<Data>.create { single in
    print("序列生成的线程")
    print(Thread.current)

    if let data = try? Data(contentsOf: URL(string: "https://www.wanandroid.com/banner/json")!) {
        single(.success(data))
    }else {
        single(.error(SomeError()))
    }
    return Disposables.create()
 }
 /// 注意这个方法名,subscribeOn,它表示指定生成序列的线程
 .subscribeOn(ConcurrentDispatchQueueScheduler(qos: .userInitiated))
复制代码

在生成序列的末尾添加一个方法,这个方法叫做subscribeOnsubscribeOnsubscribeOn

重要的事情说三遍!!!

明明是我希望在指定线程生成序列,但是方法名却是subscribeOn

虽然我看了有点晕,但是确实如此。让我们看看,指定序列生成线程之后,日志的打印情况吧:

序列生成的线程
<NSThread: 0x2808f8e80>{number = 5, name = (null)}

subscribe处理的线程
<NSThread: 0x2808f8e80>{number = 5, name = (null)}
复制代码

咦?感觉有点不对啊,怎么序列生成的线程和subscribe处理的线程都变成了非主线程了,我还是希望subscribe处理的线程在主线程呀!

别急,我们在生成序列的subscribeOn后面在追加一个方法:

let single = Single<Data>.create { single in
    print("序列生成的线程")
    print(Thread.current)

    if let data = try? Data(contentsOf: URL(string: "https://www.wanandroid.com/banner/json")!) {
        single(.success(data))
    }else {
        single(.error(SomeError()))
    }
    return Disposables.create()
 }
 /// 注意这个方法名,subscribeOn,它表示指定生成序列的线程
 .subscribeOn(ConcurrentDispatchQueueScheduler(qos: .userInitiated))
 /// 表示被订阅的时候,在主线进行处理
 .observeOn(MainScheduler.instance)
复制代码

真正指定在subscribe中处理序列的函数叫做observeOn,它表示single.subscribe { data in }所在的线程,我们再来看看此时打印的线程信息:

序列生成的线程
<NSThread: 0x280b199c0>{number = 4, name = (null)}

subscribe处理的线程
<NSThread: 0x280b48e00>{number = 1, name = main}
复制代码

参考文档

Schedulers - 调度器

很好,达到预期效果,序列在非主线程完成,subscribe处理的主线程完成。

总结

我为啥有写这篇文章?因为指定序列生成线程的函数、指定订阅序列处理线程的函数的命名实在有些奇怪,特别是序列生成线程的函数,简直就望文生义了,结果啪啪啪的打脸。

注意事项:

  • 不要被函数的名字干扰了其功能。

  • 一般情况下序列的生成与序列的订阅处理都在主线程中。

  • 如果序列的生成指定了线程,最好订阅处理也指定线程,否则会序列的生成指定线程也会是订阅处理的线程。

  • 经过验证,如果订阅处理指定了线程,而序列的生成没有做设置,并不会对序列的生成的线程有影响,依旧是主线程生成:

let single = Single<Data>.create { single in
    print("序列生成的线程")
    print(Thread.current)

    if let data = try? Data(contentsOf: URL(string: "https://www.wanandroid.com/banner/json")!) {
        single(.success(data))
    }else {
        single(.error(SomeError()))
    }
    return Disposables.create()
 }
 .observeOn(ConcurrentDispatchQueueScheduler(qos: .userInitiated))

复制代码

日志打印:

序列生成的线程

<NSThread: 0x2803bce00>{number = 1, name = main}

subscribe处理的线程

<NSThread: 0x2803e9740>{number = 4, name = (null)}
复制代码

求指导

其实Rx中,在哪些业务下面要使用调度器我确实不了解,还希望有掘友能指点一二,感谢!

另外想说的是,可能就我觉得这命名有问题,大伙都觉得挺好的,那么就让大家见笑了。

结语

我在掘金里以RxSwift为关键词搜索了文章并进行粗略的阅读,感觉自己写的有点班门弄斧,人家都是在深入底层,而我只是在API上面打仗。

很惭愧,需要更多的学习与沉淀。

RxSwift相关的,我估计暂时不会继续更文了。

RxSwift编写wanandroid客户端现已开源

目前RxSwift编写wanandroid客户端已经开源了——项目链接

猜你喜欢

转载自juejin.im/post/7031335896355962887