Firebase —初始获取期间数据不同步
退出了一家主要使用专有技术的大公司之后,我获得了所有自由,可以尝试各种出色的工具来为自己的公司构建产品。 我想在这里分享我的学习历程(在所有方面,而不仅仅是编码),我欢迎大家的评论和讨论,因为这也是我的学习方式。
最近,我一直在iOS上使用Firebase。 对于大多数事情来说,这都是令人惊奇的,我特别喜欢脱机功能。 基本上,它使您的应用程序可以脱机运行,并且在您恢复网络后,数据将立即同步到db。 它还“聪明地”(稍后Database.database().isPersistence
首先从缓存中查找数据以提高加载性能,而仅用一行代码Database.database().isPersistence
获得全部Database.database().isPersistence
。 一切工作正常,除了在初始访存时遇到神秘的数据不同步问题。
如果进行“如何刷新Firebase缓存”或“ Firebase中的缓存中的过时数据”之类的搜索,您将得到诸如此类,此类或此类的零碎讨论。 它们中的许多有用,但没有一个能提供完整的图片。 我真的想知道发生了什么,所以我做了很多实验,让我在这里总结一下自己的理解。 (TL; DR;)
有2种主要的Firebase API用于检索数据:
-
ref.observe(.value, with: { // completion block }
-
ref.observeSingleEvent(of: .value, with: { // completion block }
第一个API是事件监听器。 它返回初始结果,并监听ref
更改。 第二个API不监听更改,它只检索一次数据,因此它似乎是用于初始提取的正确API。 但是不幸的是,如果持续打开它就无法工作。
无论调用多少次, observeSingleEvent
都将仅返回本地缓存的数据。 如果您的本地数据超过默认的最大10MB,问题将变得更加严重,Firebase将启动逐出缓存的数据(似乎是随机的)。 因此,如果您离线或关闭应用程序并重新启动,则仅通过使用observeSingleEvent
获取数据可能会丢失一些数据。
这看起来像是一个常见问题,我希望有一些简单的解决方案,但令人惊讶的是,我确实找不到解决该问题的好方法。 经过大量的阅读和实验后,我提供了一些替代方案来解决或缓解此问题,并让我与您分享。
ref.observe
始终使用 ref.observe
这是最推荐的方法,也是Google小组的Firebase工程师推荐的方法。 使用observe
将使本地数据保持同步,如果使用它来获取数据,它将首先从本地获取数据,并最终与服务器同步(具有一个或多个回调)。 但是,使用它初始获取时有两个问题:
- 您无法确定何时从初始服务器状态获取数据。 如果您真的想立即进入服务器状态,请停止加载微调器,而不是让数据不断更新和重新加载,则不能这样做。
- 它不适用于列表数据。 对于列表数据,您可能会使用
.childAdded
或.childRemoved
因为.value
仅在更改时为您提供整个Blob数据。 它效率不高,大多数时候不是很有用。
由于您不知道它何时确切返回初始服务器状态,因此,如果您只想将其用于初始提取,则无法确定何时删除.value
侦听器。
ALT2 —调用 ref.keepSynced(true)
它的作用是使ref
本地数据保持同步,即使您没有附加任何侦听器也是如此。 在您获取了初始数据之后,这可能会有所帮助,基本上,如果您调用了keepSynced
则可以使用observeSingleEvent
来获取相应ref
的最新数据。 但是由于当数据完全同步时它不提供回调,因此您将遇到与ALT1类似的问题。 您可以做的一件事是使应用程序休眠几秒钟,并希望所有数据同步。 (多数情况下应该这样,否则您一开始会加载太多数据)
该API也有一些缺点。 1)。 如果不需要,始终保持加载数据效率不高; 2)。 keepSynced(true)
,本地缓存限制将不适用,我不确定这是否是您想要的。
ALT3-一种结合了以上内容的hack(基于这篇 文章 )
根据我的实验,这是始终保持一致的方式:
- 第一次调用
ref.keepSynced(true)
- 然后对
ref
进行虚拟写入,然后在完成回调中进行一次提取,即
ref.keepSynced(true)
ref.child(“ dummy”)。setValue(“ dummy”,{(错误,dummyRef)在
如果让错误==错误{
//处理错误
}
//删除哑数值
dummyRef.remove()
//如果需要,删除保持同步
ref.keepSynced(false)
//执行抓取
ref.observeSingleEvent(of:.value,with:{snapshot in
//在完成块中完成您的工作
})
})
也许在内部在同步完成之前不会触发写完成回调,但这完全是凭经验的,因此没有保证。
现在,我主要是使用这种黑客程序来进行初始抓取,到目前为止,该应用程序很高兴。 如果您遇到类似我的问题,希望能对您有所帮助…
聚苯乙烯
1.有些使用runTransaction进行runTransaction
也可以检索同步的数据,但我的实验并非如此。
2. Google可能会将重点转移到构建FireStore(仍处于Beta版中),我将尝试是否仍然存在该问题。
更新(11/27/2017):
我在新的FireStore中尝试了相同的方案,没有这样的问题,太好了!