【问题标题】:android - querying keys of each node from list with Firebaseandroid - 使用 Firebase 从列表中查询每个节点的键
【发布时间】:2018-10-13 15:50:55
【问题描述】:

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

【问题讨论】:

    标签: android firebase asynchronous firebase-realtime-database


    【解决方案1】:

    基本上,您尝试从异步 API 同步返回值。这不是一个好主意。您应该按预期异步处理 API。暂停线程可能会解决您的问题,但只能解决部分问题。而且根本不推荐。

    Firebase API 是异步,这意味着onDataChange() 方法在被调用后立即返回,并且它返回的Task 的回调将在稍后调用。无法保证需要多长时间。因此,在数据可用之前可能需要几百毫秒到几秒钟。因为该方法立即返回,所以来自数据库的值还没有从回调中填充。

    一个快速解决这个问题的方法是只在回调中使用这些值,否则我建议你从这个 post 中查看我的答案的最后一部分,其中我已经解释了它是如何实现的可以使用自定义回调来完成。您也可以看看这个 video 以获得更好的理解。

    总而言之,没有办法将异步 API 转换为 Firebase 的同步 API。

    【讨论】:

    • 嗨穆斯塔法!你有没有试过我上面的解决方案,它有效吗?
    • 嘿亚历克斯,不,我没有。因为我需要在同一方法中使用该回调的功能。我已经使用了一个监听器来了解我的列表是否可以使用。但我的问题是我还需要通知我的第一个循环,因为我不能在相同的方法中使用回调,它会在我的听众有机会知道它是否准备好之前立即返回。所以如果不能把它变成同步结构,而且我需要在同一个方法中回调的功能,我想怎么办?
    • 要使用回调中的所有逻辑,正如我上面的答案中所解释的那样,对吧?
    • 是的。但是,我正在尝试通过立即使该方法递归来使其工作。我认为这是我想要的解决方案。
    • 先生,您的回答与我所问的无关。我已经找到了递归和侦听器组合的解决方案,并且已经发布了答案。感谢您尝试提供帮助)
    【解决方案2】:

    为了解决这个问题,我让我的方法递归并使用了一个监听器。 在我的解决方案中,我们将跟踪我们调用递归方法的次数,以及 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);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-09-30
      • 2017-12-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-09
      相关资源
      最近更新 更多