Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/firebase/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/search/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android Firebase脱机功能和addListenerForSingleValueEvent_Android_Firebase_Firebase Realtime Database - Fatal编程技术网

Android Firebase脱机功能和addListenerForSingleValueEvent

Android Firebase脱机功能和addListenerForSingleValueEvent,android,firebase,firebase-realtime-database,Android,Firebase,Firebase Realtime Database,每当我将addListenerForSingleValueEvent与setPersistenceEnabled(true)一起使用时,我只会设法从服务器上获取DataSnapshot的本地脱机副本,而不更新后的DataSnapshot 但是,如果我将addValueEventListener与setPersistenceEnabled(true)一起使用,我可以从服务器获取DataSnapshot的最新副本 这对于addListenerForSingleValueEvent是否正常,因为它只在

每当我将
addListenerForSingleValueEvent
setPersistenceEnabled(true)
一起使用时,我只会设法从服务器上获取
DataSnapshot
的本地脱机副本,而更新后的
DataSnapshot

但是,如果我将
addValueEventListener
setPersistenceEnabled(true)
一起使用,我可以从服务器获取
DataSnapshot
的最新副本

这对于
addListenerForSingleValueEvent
是否正常,因为它只在本地(脱机)搜索
DataSnapshot
,并在成功检索
DataSnapshot
一次(脱机或联机)后删除其侦听器?

持久性的工作原理 Firebase客户端在内存中保留您正在积极侦听的所有数据的副本。一旦最后一个侦听器断开连接,数据将从内存中刷新

如果在Firebase Android应用程序中启用磁盘持久性,请执行以下操作:

Firebase.getDefaultConfig().setPersistenceEnabled(true); 
Firebase客户端将保留应用程序最近监听的所有数据的本地副本(磁盘上)

附加侦听器时会发生什么 假设您拥有以下
ValueEventListener

ValueEventListener listener = new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot snapshot) {
        System.out.println(snapshot.getValue());
    }

    @Override
    public void onCancelled(FirebaseError firebaseError) {
        // No-op
    }
};
ValueEventListener
添加到位置时:

ref.addValueEventListener(listener); 
// OR
ref.addListenerForSingleValueEvent(listener); 
ref.addListenerForSingleValueEvent(listener);
如果该位置的值位于本地磁盘缓存中,Firebase客户端将立即从本地缓存中调用该值的
onDataChange()
。If随后还将启动与服务器的检查,以请求对值进行任何更新。如果自上次将数据添加到缓存后,服务器上的数据发生了更改,则随后可能再次调用
onDataChange()

当您使用addListenerForSingleValueEvent时会发生什么情况 将单值事件侦听器添加到同一位置时:

ref.addValueEventListener(listener); 
// OR
ref.addListenerForSingleValueEvent(listener); 
ref.addListenerForSingleValueEvent(listener);
Firebase客户端将(与前一种情况一样)立即调用
onDataChange()
以获取本地磁盘缓存中的值。它将不再调用
onDataChange()
,即使服务器上的值不同。请注意,更新后的数据仍将被请求并在后续请求中返回

这在前面的章节中已经介绍过

解决方案和解决办法 最好的解决方案是使用,而不是使用单值事件侦听器。常规值侦听器将从服务器获取即时本地事件和潜在更新

第二个解决方案是使用新的(在2021年初引入),它没有这种问题行为。请注意,此方法始终尝试首先从服务器获取值,因此需要更长的时间才能完成。如果您的值从未更改,那么最好使用
addListenerForSingleValueEvent
(但在这种情况下,您可能不会出现在本页上)


作为一种解决方法,您还可以在使用单值事件侦听器的位置上使用。这确保了数据在每次更改时都会更新,这大大提高了单值事件侦听器看到当前值的可能性。

您可以创建事务并中止它,然后在联机(联机数据)或脱机(缓存数据)时调用onComplete

我之前创建了一个函数,该函数只有在数据库连接lomng足以进行同步时才起作用。我通过添加超时修复了这个问题。我将对此进行研究,并测试其是否有效。也许将来,当我有空的时候,我会创建android lib并发布它,但到那时,它就是kotlin中的代码:

/**
     * @param databaseReference reference to parent database node
     * @param callback callback with mutable list which returns list of objects and boolean if data is from cache
     * @param timeOutInMillis if not set it will wait all the time to get data online. If set - when timeout occurs it will send data from cache if exists
     */
    fun readChildrenOnlineElseLocal(databaseReference: DatabaseReference, callback: ((mutableList: MutableList<@kotlin.UnsafeVariance T>, isDataFromCache: Boolean) -> Unit), timeOutInMillis: Long? = null) {

        var countDownTimer: CountDownTimer? = null

        val transactionHandlerAbort = object : Transaction.Handler { //for cache load
            override fun onComplete(p0: DatabaseError?, p1: Boolean, data: DataSnapshot?) {
                val listOfObjects = ArrayList<T>()
                data?.let {
                    data.children.forEach {
                        val child = it.getValue(aClass)
                        child?.let {
                            listOfObjects.add(child)
                        }
                    }
                }
                callback.invoke(listOfObjects, true)
            }

            override fun doTransaction(p0: MutableData?): Transaction.Result {
                return Transaction.abort()
            }
        }

        val transactionHandlerSuccess = object : Transaction.Handler { //for online load
            override fun onComplete(p0: DatabaseError?, p1: Boolean, data: DataSnapshot?) {
                countDownTimer?.cancel()
                val listOfObjects = ArrayList<T>()
                data?.let {
                    data.children.forEach {
                        val child = it.getValue(aClass)
                        child?.let {
                            listOfObjects.add(child)
                        }
                    }
                }
                callback.invoke(listOfObjects, false)
            }

            override fun doTransaction(p0: MutableData?): Transaction.Result {
                return Transaction.success(p0)
            }
        }

在启用持久性的情况下工作时,我计算了侦听器接收到onDataChange()调用并停止侦听的次数,共2次。为我工作,也许有帮助:

private int timesRead;
private ValueEventListener listener;
private DatabaseReference ref;

private void readFB() {
    timesRead = 0;
    if (ref == null) {
        ref = mFBDatabase.child("URL");
    }

    if (listener == null) {
        listener = new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                //process dataSnapshot

                timesRead++;
                if (timesRead == 2) {
                    ref.removeEventListener(listener);
                }
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
            }
        };
    }
    ref.removeEventListener(listener);
    ref.addValueEventListener(listener);
}

所以我有一个有效的解决方案。您所要做的就是使用ValueEventListener,并在0.5秒后删除该侦听器,以确保在需要时已获取更新的数据。实时数据库有很好的延迟,所以这是安全的。参见下面的安全代码示例

public class FirebaseController {

private DatabaseReference mRootRef;
private Handler mHandler = new Handler();

private FirebaseController() {
    FirebaseDatabase.getInstance().setPersistenceEnabled(true);

    mRootRef = FirebaseDatabase.getInstance().getReference();
}

public static FirebaseController getInstance() {
    if (sInstance == null) {
        sInstance = new FirebaseController();
    }
    return sInstance;
}
然后使用“addListenerForSingleEvent”的方法

即使他们在执行处理程序之前关闭应用程序,它也会被删除。 编辑:可以对其进行抽象,以跟踪HashMap中添加和删除的侦听器,使用引用路径作为键,datasnapshot作为值。您甚至可以包装一个fetchData方法,该方法有一个布尔标志表示“一次”。如果这是真的,它将执行此解决方法以获取一次数据,否则它将继续正常运行。
不客气

谢谢你的完美说明。但是keepSynced(true)和addValueEventListener将始终保持打开的连接。与keepSynced(false)和addListenerForSingleValueEvent不同,firebase将允许在一段时间后断开连接。如何强制执行一次手动更新?这是一种多么不令人信服的行为,它使得测试几乎不可能。调用
keepSynced(true)
,然后使用
.addListenerForSingleValueEvent(listener)不起作用。在注册更改之前需要打开应用程序两次。@当您在“keepSynced”之后进行一次调用时,它实际上起作用。创建查询,例如设置keepSynced、addValue和onCompleted keepValue false以避免一直同步。然后数据被刷新(没有检查它是否每次都能正常工作)。另一个似乎有效的解决方案是我在使用keepSynced时在postA警告中给出的答案:它可能会导致带宽的巨大消耗,并带来相关成本。请参阅下面的实用说明:如果本地缓存数据与服务器数据相同,onDataChange将只调用一次,因此“ref.removeEventListener(listener);”将不会被执行请参阅我的工作解决方案