android-使用Firebase

时间:2018-10-13 15:50:55

标签: android firebase asynchronous firebase-realtime-database

我有一个字符串列表,此列表包含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);
}

2 个答案:

答案 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);