四、RxSwift调度者
4.1 案例引入
Schedulers
是RxSwift实现多线程的核心模块。它主要用于控制任务在哪个线程或队列运行。
咱们在平时的开发过程中,肯定都使用过网络请求,网络请求是在后台执行的,获取到数据之后,再在主线程更新UI。
先来一段代码引入
/// 子线程
DispatchQueue.global().async {
print("请求数据")
let _ = self.actionBtn.rx.tap
.subscribe(onNext: { () in
/// 主线程(通过线程调度,调度回来,切换线程)
print("tap =--- \(Thread.current)")
})
}
再来一段网络请求伪代码。
DispatchQueue.global(qos: .userInitiated).async {
let data = try? Data(contentsOf: url)
DispatchQueue.main.async {
// 更新UI
}
}
如果用RxSwift来实现上面的网络请求,则大致是这样的:
let rxData: Observable<Data> = ...
rxData
.subscribeOn(ConcurrentDispatchQueueScheduler(qos: .userInitiated))
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self](data) in
// 更新UI
})
.disposed(by: disposeBag)
说明:
- 我们用
subscribeOn
来决定数据序列的构建函数在哪个 Scheduler 上运行。 在上面的例子中,由于获取Data
需要花费很长的时间,所以用subsribeOn
切换到 后台Scheduler 来获取Data
。这样就可以避免阻塞主线程。 - 我们用
observeOn
来决定在哪个 Scheduler 监听这个数据序列。 在上面的例子中,通过observerOn
方法切换到主线程来监听并处理结果。
4.2 MainScheduler
MainScheduler 代表 主线程。如果需要执行和UI相关的任务,就需要切换到该 Scheduler 运行。
可以清晰的知道,在初始化时,在 MainScheduler
对象的内部,绑定了主队列 DispatchQueue.main
。
4.3 SerialDispatchQueueScheduler
SerialDispatchQueueScheduler 抽象了 串行DispatchQueue。如果需要执行一些串行任务,可以切换到这个 Scheduler 执行。
在初始化 SerialDispatchQueueScheduler
对象时,需要传入一个 DispatchQueue
,保存在 self.configuration
结构体中。
4.4 ConcurrentDispatchQueueScheduler
ConcurrentDispatchQueueScheduler 抽象了 并行DispatchQueue。如果需要执行一些并发任务,可以切换到这个 Scheduler执行。
4.5 OperationQueueScheduler
OperationQueueScheduler 抽象了 NSOperationQueue。它具备一些 NSOperationQueue 的特点。例如,可以通过设置 maxConcurrentOperationCount
来控制同时执行并发任务的最大数量。
在初始化 OperationQueueScheduler
对象时,需要传入 OperationQueue
和 优先级queuePriority
,作为初始化参数。
4.6 Scheduler的调度执行
从上一小节的几种调度器的源码可以发现,所有的调度器 Scheduler
都继承自 ImmediateSchedulerType
协议。
而这个协议只声明了一个 schedule
方法,而通过注释可以知道,在调度器调度执行的时候都会调用这个 schedule
方法。
咱们现在以 SerialDispatchQueueScheduler
调度器为例:
/**
Schedules an action to be executed immediately.
- parameter state: State passed to the action to be executed.
- parameter action: Action to be executed.
- returns: The disposable object used to cancel the scheduled action (best effort).
*/
public final func schedule<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable {
return self.scheduleInternal(state, action: action)
}
func scheduleInternal<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable {
return self.configuration.schedule(state, action: action)
}
/**
Schedules an action to be executed.
- parameter state: State passed to the action to be executed.
- parameter dueTime: Relative time after which to execute the action.
- parameter action: Action to be executed.
- returns: The disposable object used to cancel the scheduled action (best effort).
*/
public final func scheduleRelative<StateType>(_ state: StateType, dueTime: RxTimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable {
return self.configuration.scheduleRelative(state, dueTime: dueTime, action: action)
}
/**
Schedules a periodic piece of work.
- parameter state: State passed to the action to be executed.
- parameter startAfter: Period after which initial work should be run.
- parameter period: Period for running the work periodically.
- parameter action: Action to be executed.
- returns: The disposable object used to cancel the scheduled action (best effort).
*/
public func schedulePeriodic<StateType>(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping (StateType) -> StateType) -> Disposable {
return self.configuration.schedulePeriodic(state, startAfter: startAfter, period: period, action: action)
}
分析以上方法会发现,最终都会调用 self.configuration
的某个方法。而且查看几种调度器的源码可以知道,Scheduler 中都有一个重要的属性 let configuration: DispatchQueueConfiguration
。其中保存了我们需要的队列和leeway信息。
那么,我们就来分析 DispatchQueueConfiguration
中的方法。
首先分析 schedule
方法,虽然 schedule
方法中只有寥寥几句代码,但是也清晰的展示其 核心逻辑:就是在当前队列下面,异步调度执行了闭包 action(state)。
func scheduleRelative<StateType>(_ state: StateType, dueTime: Foundation.TimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable {
let deadline = DispatchTime.now() + dispatchInterval(dueTime)
let compositeDisposable = CompositeDisposable()
let timer = DispatchSource.makeTimerSource(queue: self.queue)
timer.schedule(deadline: deadline, leeway: self.leeway)
// 因篇幅原因,省略部分代码 ...
timer.setEventHandler(handler: {
if compositeDisposable.isDisposed {
return
}
_ = compositeDisposable.insert(action(state))
cancelTimer.dispose()
})
timer.resume()
_ = compositeDisposable.insert(cancelTimer)
return compositeDisposable
}
func schedulePeriodic<StateType>(_ state: StateType, startAfter: TimeInterval, period: TimeInterval, action: @escaping (StateType) -> StateType) -> Disposable {
let initial = DispatchTime.now() + dispatchInterval(startAfter)
var timerState = state
let timer = DispatchSource.makeTimerSource(queue: self.queue)
timer.schedule(deadline: initial, repeating: dispatchInterval(period), leeway: self.leeway)
// 因篇幅原因,省略部分代码 ...
timer.setEventHandler(handler: {
if cancelTimer.isDisposed {
return
}
timerState = action(timerState)
})
timer.resume()
return cancelTimer
}
以上两个方法中虽然没有直接在当前队列中异步调用闭包,但是创建 timer
时,却是在当前队列中创建的,因此 timer
回调时也是在当前队列执行 eventHandler
,间接实现当前队列下的调度。