如何正确同步Rx主题`.onNext`调用?

时间:2019-01-13 12:38:11

标签: kotlin rx-java2

我正在开发消息传递应用程序,并希望提供可观察到newMessages的服务。应用程序的不同部分可以订阅新消息的可观察和显示徽章,等等。

我有两个限制:

  1. 我无法在通知有效负载中保留整个消息(某些安全限制)。相反,当我收到带有uuid的通知时,我向服务器请求带有此uuid的消息。

  2. 实际上,我向服务器询问所有新消息,从本地数据库加载最后一条消息uuid,然后向服务器询问此uuid之后的所有消息。我将所有新消息缓存在本地数据库中。

在第一个球上,我想:

  1. 初始化名为PublicSubject的{​​{1}}。

  2. 当通知到达时,请检查本地数据库中是否有带有此newMessages的消息。如果是->显示通知;如果否->从服务器加载所有新消息,将它们存储在本地数据库中,将它们发送到uuid主题中并显示通知。

类似这样的东西:

newMessages

使用这种方法,我遇到以下问题:

  • 如果两个通知一个接一个地到达,则两个都可以观察到各自的private val newMessagesSubject = PublishSubject.create<List<UserMessage>>() val newMessages = newMessagesSubject.observeOn(AndroidSchedulers.mainThread()) fun newNotificationArrived(uuid: UUID) { fun filterCurrentMessage(messages: List<UserMessage>) = messages .firstOrNull { it.uuid == uuid } ?: throw IllegalStateException("Can't find a message with id: $uuid") /* * If we manage to load from the local database, we don't emit because we assume * that the request that we emited the messages before storing them into the DB. */ fun loadFromDatabase() = Maybe.fromCallable { messageTable.getMessageWithUuid(uuid) } fun loadFromServer(accessCode: String, latestMessageUuid: UUID) = messagingApi .getUserMessagesAfterUUID(accessCode, latestMessageUuid, null) .retryWithExponentialBackoff() fun loadCacheEmitAndMap(accessCode: String, latestMessageUuid: UUID) = loadFromServer(accessCode, latestMessageUuid) .doOnSuccess { messageTable.insertNewMessages(it) } .doOnSuccess { newMessagesSubject.onNext(it) } .map(::filterCurrentMessage) Single .fromCallable { accessCodeSettings.accessCode to messageTable.latestUUID } .flatMap { (accessCode, latestMessageUuid) -> loadFromDatabase() .switchIfEmpty(loadCacheEmitAndMap(accessCode, latestMessageUuid)) } .subscribe(::showNotification) } 不在数据库中,因此这两个都将从服务器中装入新消息,可能是同一组消息,两者都将将消息保存在数据库中,两者都将在uuid主题中发出消息。因此,我将得到重复的消息。

    • 我无法newMessages提取消息的方法,因为它也是一种可观察的方法,并且我不想使其阻塞。

您将如何设计可观察的流,以确保synchronize主题正确?

1 个答案:

答案 0 :(得分:0)

您可以创建一个内部服务来处理消息请求。

fun requestMessage( message: UUID ): Single<UserMessage>

如果有必要,它将负责通过转到服务器来获取消息。在内部,它将使用由UUID索引的缓存。这是我发现有用的一种方法:

val messageCache: Map<UUID, BehaviorSubject<UserMessage>> = Maps.newConcurrentMap();

requestMessage()的实现如下:

return messageCache.computeIfAbsent(message, id -> {
   val res: BehaviorSubject.<UserMessage>create()
   getMessage(id).subscribe(res)
   return res.take(1).toSingle()
  })

(请原谅我糟糕的Kotlin语法)这将始终返回一个最终具有消息的可观察对象。 ConcurrentHashMap确保针对任何消息ID仅发出一个服务请求。