我正在开发消息传递应用程序,并希望提供可观察到newMessages
的服务。应用程序的不同部分可以订阅新消息的可观察和显示徽章,等等。
我有两个限制:
我无法在通知有效负载中保留整个消息(某些安全限制)。相反,当我收到带有uuid
的通知时,我向服务器请求带有此uuid
的消息。
实际上,我向服务器询问所有新消息,从本地数据库加载最后一条消息uuid
,然后向服务器询问此uuid
之后的所有消息。我将所有新消息缓存在本地数据库中。
在第一个球上,我想:
初始化名为PublicSubject
的{{1}}。
当通知到达时,请检查本地数据库中是否有带有此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
主题正确?
答案 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仅发出一个服务请求。