何时不应使用RxJava的Observable?

3
例如,我在我的应用程序中有一个简单的管理器,其中我试图保持一切“响应式”:
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())和返回发出一个项目的Observable(securityManager.anyPassword())。

这使我认为我不应该在appLockManager.setLocked()等方法中使用RxJava。

我应该在这里使用Observables吗? 特别是对于仅更新AppLockManager并根本不返回数据的setLocked()/setUnlocked()/resetLockTimer()方法。

2个回答

2

使用嵌套订阅是不好的代码习惯。要在一个Observable完成后使用另一个,可以使用concat操作符。

你的代码可以更简单。例如,不要使用以下方式:

.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
                       }
                })
}

是的,那是一个不错的解决方案。但我刚刚在谷歌上搜索了一下,并找到了关于Completable的信息。它在最新的RxJava更新中成为了@Beta版本,而且正好符合我的需求。 - Alexandr
“filter { it }” 不能替换“.flatMap { if(it == true) Observable.just(it) else Observable.never() }”,因为在 flatMap 中我返回了“Observable.never()”,这将永远不会调用“onComplete”。 - Alexandr
1
检查您的应用程序是否使用了never泄漏内存。(我不确定) - dwursteisen

0

dwursteisen提出的另一种解决方案是使用Completable。根据changelog,它刚刚成为@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)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接