RxJava Observable to Completable,如何避免toBlocking()

时间:2017-01-08 16:36:25

标签: java android rx-java kotlin

我目前在Android上使用RxJava与Kotlin,但我遇到了问题,我无法使用toBlocking()解决问题。

我在员工服务中有方法,它返回一个Observable>:

fun all(): Observable<List<Employee>>

这一切都很好,因为只要员工发生变化,这个Observable就会发布新的员工列表。但我想从员工那里生成PDF文件,这显然不是每次员工变更都需要运行的。另外,我想从我的PDF生成器方法返回Completable个对象。我想在我的PDF中添加标题,然后遍历员工并计算每个员工的工资,这也会返回一个Observable,这就是我现在使用toBlocking的地方。我目前的做法是:

private fun generatePdf(outputStream: OutputStream): Completable {
    return employeeService.all().map { employees ->
        try {
                addHeaderToPDF()
                for (i in employees) {
                    val calculated = employeeService.calculateWage(i.id).toBlocking().first()
                    // Print calculated to PDF....
                }
                addFooterToPDF()
                return @map Completable.complete()
            }
            catch (e: Exception) {
                return @map Completable.error(e)
            }
        }.first().toCompletable()

有没有办法让这个代码使用RxJava更清洁?

提前致谢!

2 个答案:

答案 0 :(得分:2)

免责声明:此答案正在进行中。

基本前提:如果您在流中有blocking,那么您做错了。

注意:没有州必须离开可观察的lambda。

第1步:流式传输整个数据集

输入是员工流。对于每个员工,您需要获得一份工资。让我们把它变成一个流。

/**
 * @param employeesObservable
 * Stream of employees we're interested in.
 * @param wageProvider
 * Transformation function which takes an employee and returns a [Single] of their wage.
 * @return
 * Observable stream spitting individual [Pair]s of employees and their wages.
 */
fun getEmployeesAndWagesObservable(
        employeesObservable: Observable<Employee>,
        wageProvider: Function<Employee, Single<Int>>
): Observable<Pair<Employee, Int>>? {
    val employeesAndWagesObservable: Observable<Pair<Employee, Int>>

    // Each Employee from the original stream will be converted
    // to a Single<Pair<Employee, Int>> via flatMapSingle operator.
    // Remember, we need a stream and Single is a stream.
    employeesAndWagesObservable = employeesObservable.flatMapSingle { employee ->
        // We need to get a source of wage value for current employee.
        // That source emits a single Int or errors.
        val wageForEmployeeSingle: Single<Int> = wageProvider.apply(employee)

        // Once the wage from said source is loaded...
        val employeeAndWageSingle: Single<Pair<Employee, Int> = wageForEmployeeSingle.map { wage ->
            // ... construct a Pair<Employee, Int>
            employee to wage
        }

        // This code is not executed now. It will be executed for each Employee
        // after the original Observable<Employee> starts spitting out items.
        // After subscribing to the resulting observable.
        return@flatMapSingle employeeAndWageSingle
    }

    return employeesAndWagesObservable
}

订阅时会发生什么:

  1. 从源头找一名员工。
  2. 取得雇员的工资。
  3. 吐出一对员工和他们的工资。
  4. 重复这一过程,直到employeesObservable信号onComplete或其他内容失败并显示onError

    二手经营者:

    • flatMapSingle:将实际值转换为某个转换值的新单个流。
    • map:将实际值转换为其他实际值(无嵌套流)。

    嘿,你是如何将它与你的代码挂钩的:

    fun doStuff() {
        val employeesObservable = employeeService.all()
        val wageProvider = Function<Employee, Single<Int>> { employee ->
            // Don't listen to changes. Take first wage and use that.
            employeeService.calculateWage(employee.id).firstOrError()
        }
    
        val employeesAndWagesObservable = 
                getEmployeesAndWagesObservable(employeesObservable, wageProvider)
    
        // Subscribe...
    }
    

    二手经营者:

    • first:从observable中取出第一个项目并将其转换为单个流。
    • timeout:如果你通过网络获得工资,那么一个好主意就是.timeout工资。

    后续步骤

    选项1:在此结束

    请勿订阅,请致电

    val blockingIterable = employeesAndWagesObservable.blockingIterable()
    blockingIterable.forEach { ... }
    

    以同步方式处理每个项目。坐下来,找出后续步骤,观看演示文稿,阅读示例。

    选项2:添加图层

    1. .map每个Pair<Employee, Int>到一些抽象的PDF构建块。
    2. 通过Observable.fromCallable { ... }将页眉和页脚打印机转换为Observables,让它们返回PDF构建块。
    3. 通过Observable.concat(headerObs, employeeDataObs, footerObs)
    4. 以顺序方式合并所有这些内容
    5. .subscribe此结果并开始将PDF构建块写入PDF编写器。
    6. TODO:
      • 找出一种在订阅时懒惰地初始化PDF编写器的方法(而不是在构建流之前),
      • 出错时删除输出
      • 完成或出错时关闭输出流。

答案 1 :(得分:0)

我想出了这个:

    return employeeService.all().first()
            .doOnSubscribe { addHeaderToPDF() }
            .flatMapIterable { it }
            .flatMap { employeeService.calculateWage(it.id).first() }
            .doOnNext { printEmployeeWage(it) }
            .doOnCompleted { addFooterToPDF }
            .toCompletable()

这是应该怎么做的? :)