例如,我的应用程序中有一个简单的Manager,我试图在其中保留所有reactive
:
class AppLockManager {
private val logger = LoggerFactory.getLogger(javaClass)
private val rxHelper: RxHelper
private val securityManager: DiarySecurityManager
private var locked = false
private var lastUnlockTime: LocalDateTime? = null
constructor(rxHelper: RxHelper, securityManager: DiarySecurityManager) {
this.rxHelper = rxHelper
this.securityManager = securityManager
}
fun shouldLock(): Observable<Boolean> {
return securityManager.isSecutiryEnabled()
.doOnNext { logger.debug("isSecurityEnabled: $it") }
.map { it && !locked && isLockTimerExpired() }
.doOnNext { logger.debug("shouldLock: $it") }
.compose(rxHelper.applySchedulers())
}
private fun isLockTimerExpired(): Boolean {
if(lastUnlockTime == null) return true
val timerExpiredMoment = lastUnlockTime!!.plusSeconds(30)
val now = LocalDateTime.now().isAfter(timerExpiredMoment)
val isExpired = LocalDateTime.now().isAfter(timerExpiredMoment)
logger.debug("timerExpiredMoment: $timerExpiredMoment / now: $now; isExpired: $isExpired")
return isExpired
}
fun setLocked(): Observable<Void> {
return Observable.create<Void> {
this.locked = true
it.onCompleted()
}.compose(rxHelper.applySchedulers())
}
fun setUnlocked(): Observable<Void> {
return Observable.create<Void> {
this.locked = false
lastUnlockTime = LocalDateTime.now()
}.compose(rxHelper.applySchedulers())
}
fun resetLockTimer(): Observable<Void> {
return Observable.create<Void> {
lastUnlockTime = LocalDateTime.now()
}.compose(rxHelper.applySchedulers())
}
}
这是一个简单的类,用于计算时间,并在应用程序必须锁定时从shouldLock()
发出true
。
以下是我使用它的方法:
fun lockAppIfNeeded() {
appLockManager.shouldLock()
.doOnNext { logger.debug("shouldLock: $it") }
.flatMap { if(it == true) Observable.just(it) else Observable.never() } // flow down only if it == true
.flatMap { appLockManager.setLocked() } // then lock
.subscribe(sub({}, Throwable::printStackTrace, { // use onComplete as source Observable is empty
securityManager.anyPassword().subscribe {
if (it) {
view.navigateToAskPassword() // anyPassword is true
} else {
view.navigateToFirstPasswordSetup() // anyPassword is false
}
}
}))
}
看起来很难看,不是吗?:(
我只是找不到合适的运算符来将空的Observable(appLockManager.setLocked()
(和securityManager.anyPassword()
组合在一起。
这让我相信,对于appLockManager.setLocked()
这样的方法,我不应该使用RxJava。
我应该在这里使用Observables吗特别适用于setLocked()
/setUnlocked()
/resetLockTimer()
方法,它们只更新AppLockManager
,根本不返回数据。
使用嵌套订阅是一种糟糕的代码气味。要在完成另一个Observable
之后使用另一个,可以使用concat
Observables。
你的代码可以更简单。例如,不用这个:
.flatMap { if(it == true) Observable.just(it) else Observable.never() }
您可以使用filter
。
因此,删除嵌套订阅+filter
将导致以下代码:
fun lockAppIfNeeded() {
appLockManager.shouldLock()
.doOnNext { logger.debug("shouldLock: $it") }
.filter { it } // flow down only if it == true
.flatMap { appLockManager.setLocked() } // then lock
.ignoreElements() // throw away appLockManager items
.concatWith(securityManager.anyPassword())
.subscribe {
if (it) {
view.navigateToAskPassword() // anyPassword is true
} else {
view.navigateToFirstPasswordSetup() // anyPassword is false
}
})
}
@dwursteisen提出的另一种解决方案是使用Completable
。根据变更日志,它只是变成了@Beta
。
有一种改进的lockAppIfNeeded()
方法:
fun lockAppIfNeeded() {
appLockManager.shouldLock()
.doOnNext { logger.debug("shouldLock: $it") }
.filter { it } // flow down only if it == true
.toCompletable()
.concatWith(appLockManager.setLocked()) // then lock
.andThen(securityManager.anyPassword())
.subscribe(sub {
if (it) {
view.navigateToAskPassword() // anyPassword is true
} else {
view.navigateToFirstPasswordSetup() // anyPassword is false
}
})
}
其中setLock()
返回Completable
:
fun setLocked(): Completable {
return Completable.fromAction { this.locked = true }
.compose(rxHelper.applySchedulersToCompletable())
}
此外,shoudlLock()
中的Observable
可以替换为Single
(发出一个项目的Observable
(。