我有一个字符串列表,此列表包含Firebase数据库中的节点名称。
我有一个自定义排序方法,因此我想在不使用Firebase查询排序功能的情况下获取自定义列表中每个节点的所有键。 由于这个原因,我需要检索所有数据,因此在检索某些数据时将其添加到列表中是不可行的。
但是问题始于Firebase的异步结构。
我遍历自定义列表中的每个节点名称,并在该循环下,在Firebase线程中创建另一个循环(addListenerForSingleValueEvent)以检索所有键。 但是它异步工作。我试图通过使用Thread和Semaphore将其更改为同步,但是它没有用。 我还使用了自定义接口(FirebaseDataRetrieveListener)来指示valueEventListener中的循环何时结束,但是由于valueEventListener立即返回,因此必须在不暂停线程的情况下实现。
如果可以在这种情况下使用Task,怎么可能,或者还有其他解决方案吗?
private void getKeysFromNodeList(final FirebaseDataRetrieveListener listener) //TODO [BUG] when first for loop initializes it ignores the seperate thread in the inner anonymous class so it'll be already finished it's cycle before Firebase` loop starts...
{
listener.onStart();
final DatabaseReference databaseRef = firebaseInstance.rootRef.child(context.getString(R.string.databaseref));
for (iterator = 0; iterator < nodeList.size(); iterator ++)
{
Query query = databaseRed.child(nodeList.get(iterator));
query.addListenerForSingleValueEvent(new ValueEventListener()
{
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot)
{
for (DataSnapshot snap : dataSnapshot.getChildren())
{
iterator++;
String key= snap.getKey();
// I've done my jobs with given key here
// if got the last key from node and iterated the last node
if (firebaseIterator== dataSnapshot.getChildrenCount() - 1 && iterator == nodeList.size() - 1)
{
firebaseIterator= 0;// reset the iterators
iterator= 0;
listener.onSuccess();
break;
}
// if got the last key from node
else if (firebaseIterator== dataSnapshot.getChildrenCount() - 1)
{
firebaseIterator= 0; // reset the iterator
break;
}
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError)
{
listener.onFailed(databaseError);
return;
}
});
}
;
}
}
FirebaseDataRetrieveListener.java
public interface FirebaseDataRetrieveListener
{
public void onStart();
public void onSuccess();
public void onFailed(DatabaseError databaseError);
}
答案 0 :(得分:1)
基本上,您正在尝试从异步的API同步返回值。那不是一个好主意。您应该按预期异步处理API。暂停线程可能会解决您的问题,但只能解决一部分问题。而且根本不推荐。
Firebase API是异步,这意味着onDataChange()
方法在调用后立即返回,并且它返回的Task中的回调将在一段时间后调用。无法保证需要多长时间。因此,可能需要几百毫秒到几秒钟的时间才能获得该数据。由于该方法会立即返回,因此尚未从回调中填充来自数据库的值。
此问题的快速解决方案是仅在回调中使用这些值,否则,我建议您从此 post 中查看anwser的最后一部分,其中我已经解释了如何使用自定义回调完成操作。您也可以查看此 video 以获得更好的理解。
结论是,由于Firebase变成了同步API,因此无法将异步API转换为
。答案 1 :(得分:1)
为解决此问题,我使我的方法递归使用了监听器。 在我的解决方案中,我们将跟踪我们调用递归方法的次数,以及Firebase循环遍历给定节点的键的次数,以检测我们是否到达节点的末尾,并遍历循环中的所有节点。列表。
在您的类范围内声明2个迭代器和1个侦听器。
private RecursiveListener listener;
private long firebaseIterator = 0;
private int recursiveIterator = 0;
public interface getKeysFromNodeListListener
{
void onFinished();
}
我们将使用递归侦听器来通知我们的递归功能是否已完成。
在调用方法之前,请定义侦听器并覆盖onFinished()。 当我们都遍历所有节点及其键时,将调用onFinished。
private void getKeysFromNodeList(final getKeysFromNodeListListener listener)
{
final DatabaseReference databaseRef = firebaseInstance.rootRef.child(database_reference);
if (recursiveIterator < nodesList.size())
{
Query query = databaseRef.child(nodesList.get(recursiveIterator));
query.addListenerForSingleValueEvent(new ValueEventListener()
{
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot)
{
for (DataSnapshot snap : dataSnapshot.getChildren())
{
firebaseIterator++;
//Do you job with the current key
String key = snap.getKey();
keysList.add(key);
// if got the last key from key and iterated the last node
if (firebaseIterator == dataSnapshot.getChildrenCount() && recursiveIterator == nodeList.size() -1)
{
firebaseIterator = 0;// reset the iterators
recursiveIterator = 0;
return;
}
// if got the last key from current node
else if (firebaseIterator == dataSnapshot.getChildrenCount() )
{
recursiveIterator++; // increase the recursive iterator because we looped through all the keys under the current node
firebaseIterator = 0; // reset the recursiveIterator because we gonna need to loop again with default value
getKeysFromNodeList(listener); // call the same method
}
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError)
{
Log.e(TAG, "Firebase query failed " + databaseError);
return;
}
});
}
}
使用该方法时,首先创建一个侦听器并覆盖onFinished(),然后调用该方法。
listener = new getKeysFromNodeListListener()
{
@Override
public void onFinished()
{
// Do whatever you gonna do after you got all the keys from all nodes.
}
};
getKeysFromNodeList(listener);