【问题标题】:When not to use RxJava's Observable?什么时候不使用 RxJava 的 Observable?
【发布时间】:2016-09-19 01:02:06
【问题描述】:

例如,我的应用程序中有一个简单的管理器,我试图在其中保留所有内容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() wwitch return Observable 结合起来,它会发出一个项目。

这让我相信我不应该将 RxJava 用于像 appLockManager.setLocked() 这样的方法。

我应该在这里使用 Observables 吗? 专门用于 setLocked()/setUnlocked()/resetLockTimer() 方法,它只更新 AppLockManager 并且根本不返回任何数据。

【问题讨论】:

    标签: java android rx-java kotlin rx-android


    【解决方案1】:

    使用嵌套订阅是不好的代码气味。要在另一个完成后使用另一个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
                           }
                    })
    }
    

    【讨论】:

    • 是的,这是一个很好的解决方案。但我只是用谷歌搜索了一下,找到了关于Completable 的信息。它在上次 RxJava 更新中变成了 @Beta,它符合我的需求。
    • filter { it } 不会像在 flatMap 中那样替换 .flatMap { if(it == true) Observable.just(it) else Observable.never() } 我返回 Observable.never() 永远不会调用 onComplete
    • 检查您的应用程序不会泄漏内存。 (我不确定)
    【解决方案2】:

    @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 可以替换为 SingleObservable 发出一项)。

    【讨论】:

      猜你喜欢
      • 2014-03-20
      • 1970-01-01
      • 2014-05-15
      • 1970-01-01
      • 1970-01-01
      • 2022-11-10
      相关资源
      最近更新 更多