我将我的一个应用程序转换为新的Firestore。我正在做一些事情,比如在点击按钮上保存文档,然后在onSuccess
监听器中,进行不同的活动。
我还使用Firestore保存操作返回任务的事实,使用Tasks.whenAll
将任务组合在一起:
val allTasks = Tasks.whenAll(
createSupporter(supporter),,
setStreetLookup(makeStreetKey(supporter.street_name)),
updateCircleChartForUser(statusChange, createMode = true),
updateStatusCountForUser(statusChange))
allTasks.addOnSuccessListener(this@SignUpActivity, successListener)
allTasks.addOnFailureListener(this@SignUpActivity, onFailureListener)
最后,我从成功保存中获取文档ID并将其存储在首选项或本地数据库中供以后使用(在onSuccessListener
中)
一切都很好。直到失去网络连接。然后一切都崩溃了,因为任务永远不会完成,onSuccess / onFailure / onComplete监听器永远不会被调用。所以该应用程序只是挂起。
我通过在每次保存之前检查网络可用性来解决这个问题,然后通过创建没有任何监听器的任务来解决这个问题。我还使用UUID生成器在本地生成文档ID。
这个,BTW,不是应用程序与旧的firebase一起工作的方式。在这种情况下,离线时一切运行良好,我看到文件在应用程序上线时同步。
我对Firestore的解决方法似乎是一个可怕的黑客。有没有人想出更好的解决方案?
见相关的Firestore database on insert/delete document callbacks not being invoked when there is no connection addOnCompleteListener not called offline with cloud firestore
Cloud Firestore为我们提供了处理脱机数据的功能,但您需要使用“快照”(QuerySnapshot,DocumentSnapshot)来处理这种情况,遗憾的是它没有很好地记录。这是使用Snapshot处理案例的一些代码示例(我使用Kotlin Android):
更新数据:
db.collection("members").document(id)
.addSnapshotListener(object : EventListener<DocumentSnapshot> {
override fun onEvent(snapshot: DocumentSnapshot?,
e: FirebaseFirestoreException?) {
if (e != null) {
Log.w(ContentValues.TAG, "Listen error", e)
err_msg.text = e.message
err_msg.visibility = View.VISIBLE;
return
}
snapshot?.reference?.update(data)
}
})
添加数据:
db.collection("members").document()
.addSnapshotListener(object : EventListener<DocumentSnapshot> {
override fun onEvent(snapshot: DocumentSnapshot?,
e: FirebaseFirestoreException?) {
if (e != null) {
Log.w(ContentValues.TAG, "Listen error", e)
err_msg.text = e.message
err_msg.visibility = View.VISIBLE;
return
}
snapshot?.reference?.set(data)
}
})
删除数据:
db.collection("members").document(list_member[position].id)
.addSnapshotListener(object : EventListener<DocumentSnapshot> {
override fun onEvent(snapshot: DocumentSnapshot?,
e: FirebaseFirestoreException?) {
if (e != null) {
Log.w(ContentValues.TAG, "Listen error", e)
return
}
snapshot?.reference?.delete()
}
})
你可以在这里看到代码示例:https://github.com/sabithuraira/KotlinFirestore和博客文章http://blog.farifam.com/2017/11/28/android-kotlin-management-offline-firestore-data-automatically-sync-it/
当网络连接丢失时(用户设备上没有网络连接),onSuccess()
和onFailure()
都不会被触发。这种行为是有道理的,因为只有在Firebase服务器上提交(或拒绝)数据时才会认为该任务已完成。只有在任务完成时才会调用onComplete(Task<T> task)
方法。因此,如果没有互联网连接,则不会触发onComplete
。
每次保存前无需检查网络可用性。有一种解决方法可以帮助您轻松查看Firestore客户端是否确实无法连接到Firebase服务器,即enabling debug logging
:
FirebaseFirestore.setLoggingEnabled(true);
将数据写入Firestore数据库的操作在实际提交到后端后定义为signal completion
。因此,这可以按预期工作:离线时,它们不会发出完成信号。
请注意,即使您没有等待从删除完成任务,Firestore客户端也会在内部保证您可以读取自己的写入。
我发现如何使用http://blog.farifam.com的信息来做到这一点。基本上你必须使用SnapshotListeners
而不是OnSuccess
听众进行离线工作。此外,您无法使用Google的任务,因为他们不会离线竞争。
相反(因为任务基本上是Promises),我使用了可以将听众附加到承诺的Kotlin Kovenant库。一个问题是你必须配置Kovenant以允许多个分辨率的承诺,因为事件监听器可以被调用两次(一旦数据被添加到本地缓存,一次被同步到服务器)。
下面是一个代码片段,包含成功/失败侦听器,可以在线和离线运行。
val deferred = deferred<DocumentSnapshot, Exception>() // create a deferred, which holds a promise
// add listeners
deferred.promise.success { Log.v(TAG, "Success! docid=" + it.id) }
deferred.promise.fail { Log.v(TAG, "Sorry, no workie.") }
val executor: Executor = Executors.newSingleThreadExecutor()
val docRef = FF.getInstance().collection("mydata").document("12345")
val data = mapOf("mykey" to "some string")
docRef.addSnapshotListener(executor, EventListener<DocumentSnapshot> { snap: DocumentSnapshot?, e: FirebaseFirestoreException? ->
val result = if (e == null) Result.of(snap) else Result.error(e)
result.failure {
deferred.reject(it) // reject promise, will fire listener
}
result.success { snapshot ->
snapshot.reference.set(data)
deferred.resolve(snapshot) // resolve promise, will fire listener
}
})