让我们想象一下以下情况,我们有一个从服务器端返回Observable对象源的函数:
private fun getStatistics(): Observable<TestStatistics> {
return Observable
.fromIterable(listOf(
TestStatistics(1.1, 1.2, 4),
TestStatistics(2.1, 2.2, 1),
TestStatistics(3.1, 3.2, 99)
))
.delay(2, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
}
TestStatistics实体:
data class TestStatistics(val doubleCashBack: Double, val doubleAmount: Double, val currencyId: Int)
如您在服务器响应中看到的,我们有currencyId指向货币实体:
data class TestCurrency(val currencyId: Int, val currencySign: String)
我们还有另一个函数,可以从数据库中按ID返回货币实体的单一来源:
private fun getCurrencyById(id: Int): Single<TestCurrency> {
return when (id) {
1 -> Single.just(TestCurrency(1, "!"))
2 -> Single.just(TestCurrency(2, "@"))
3 -> Single.just(TestCurrency(3, "#"))
else -> Single.error(Exception("Currency not found"))
}
.delay(1, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
}
主要思想是获取每个发出的Statistic实体,将它们的属性组合在一起,而不是将包含属性和Currency的组合实体作为对象,因此出现了问题,在这种情况下,我们必须采用currencyId作为第一个成功接收的Currency数据库中的对象,然后发出结果实体,因此结果类如下所示:
data class TestDashboardStatistics(val count: Int, val cashBack: Double, val amount: Double, val testCurrency: TestCurrency)
但是这种可观察到的资源组合存在一些问题,服务器请求在一个线程中运行,数据库在另一个线程中运行,并且在第三线程中组合代码,因此我必须确保我将处理从服务器接收到的所有统计信息,并将忽略所有错误从数据库返回(仅当我最终找到货币时,如果所有请求均失败,则必须返回默认值),并且仅向数据库发出一个成功请求,将此对象放入结果实体并将其返回 梳理功能可能如下:
private fun getCombinedStatistics(): Single<TestDashboardStatistics> {
return Single.create<TestDashboardStatistics> {
var transactionsAmount = 0.0
var cashBackAmount = 0.0
var count = 0
var currency = TestCurrency(-1, "default")
getStatistics().subscribe({ statistic ->
++count
transactionsAmount += statistic.doubleAmount
cashBackAmount += statistic.doubleCashBack
getCurrencyById(statistic.currencyId).subscribe({ cur ->
// TODO do not request currency for future statistics because we have it now but
// TODO because different threads we can subscribe for new request before we will receive this result
currency = cur
}, { err ->
// TODO ignore error if there is a hope that other statistics will have valid currency code
})
}, {
// On requesting statistics error just throw it up
Single.error<TestDashboardStatistics>(it)
}, {
// When all statistics will be received and precessed emit result
// But it could be called even before we will receive any response from database
Single.just(TestDashboardStatistics(count, cashBackAmount, transactionsAmount, currency))
})
}
}
我想到的一个解决方案是以某种方式从数据库请求货币,从而阻塞了处理统计信息,因此处理将一直等到db请求完成后再转到另一个,但是我对Rx运算符的了解很差,所以我不知道该怎么办。
答案 0 :(得分:0)
我建议按照您的建议将数据库请求保留为阻塞状态
data class TestStatistics(val doubleCashBack: Double, val doubleAmount: Double, val currencyId: Int)
data class TestCurrency(val currencyId: Int, val currencySign: String)
data class TestDashboardStatistics(val count: Int?, val cashBack: Double, val amount: Double, val testCurrency: TestCurrency)
object Helloworld {
private fun getStatistics(): Observable<TestStatistics> {
return Observable
.fromIterable(listOf(
TestStatistics(1.1, 1.2, 4),
TestStatistics(2.1, 2.2, 1),
TestStatistics(3.1, 3.2, 99),
TestStatistics(4.1, 4.3, 2),
TestStatistics(5.1, 5.3, 3)
))
.delay(2, TimeUnit.SECONDS)
}
private fun getCurrencyById(id: Int): TestCurrency? {
// blocking call
return when (id) {
1 -> TestCurrency(1, "!")
2 -> TestCurrency(2, "@")
3 -> TestCurrency(3, "#")
else -> null
}
}
@JvmStatic
fun main(args: Array<String>) {
getStatistics()
.map { getCurrencyById(it.currencyId) to it }
.filter { it.first != null }
.map { TestDashboardStatistics(null, it.second.doubleCashBack, it.second.doubleAmount, it.first!!) }
.subscribe { println(it) }
Thread.sleep(5000)
}
}
我将count
字段设置为可空,因为我不完全了解您要实现的目标。
我还建议您不要使用辅助方法中的subscribeOn
调用,而是将其放入main
方法中(连同observeOn()
函数一起使用),在此您可以链接业务逻辑。这样,您可以在不同的操作之间切换线程(例如,在ui
线程上进行订阅,在io
线程上进行数据库调用,在computation
线程上执行繁重的算法,等等)>
希望这会有所帮助:)
P.S。据我了解您的用例,您所需要的只是一个简单的map
操作:TestStatistics
-> TestDashboardStatistics
。如果不想每次都TestCurrency
进入数据库,则可以缓存已经获取的实例(使用Map
?)。