有什么方法可以让我的应用程序在继续执行代码之前等待从Firebase检索数据?

时间:2019-07-19 16:35:37

标签: java android firebase google-cloud-firestore

我的应用程序必须在ListView上显示名称列表。这些名称以以下方式存储在Cloud Firestore中:

集合:用户-文档:由用户UID整理-字段:名称(我必须注意,每个用户还有其他字段,但是我需要专门检索名称字段)

要完成此任务,我有第一个列表来检索所有文档或用户UID。然后,该第一个列表将在for循环中使用,以检索用户集合中每个用户的名称。

但是,由于Firebase异步检索数据,通常会丢失一些名称,并且最终它们会以混乱的方式显示(与从第一个列表传递uid的顺序不一致)。

如果有人可以让我对如何使Firebase等待数据被检索之前继续进行for循环有任何见解,将不胜感激!

下面是我的一些代码,可以使您更好地了解自己在做什么。

代码的第一部分,该代码成功检索了所有文档(uid)并将它们放在列表中

subTopicsDatabase.collection("schoolTopics").document(docKey).get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
                @Override
                public void onComplete(@NonNull Task<DocumentSnapshot> task) {

                    if (task.isSuccessful()){

            DocumentSnapshot document = task.getResult();

            if (document.exists()) {

                List<String> list = new ArrayList<>();

                Map<String, Object> map = document.getData();
                if (map != null) {
                    for (Map.Entry<String, Object> entry : map.entrySet()) {
                        list.add(entry.getValue().toString());
                    }

                }

                }});

代码的第二部分,由于Firebase的异步行为而无法正常工作。


for (int i = 0; i<list.size(); i++) {

                        String uid = list.get(i);

                        Toast.makeText(TutorsListActivity.this, uid, Toast.LENGTH_LONG).show();

                        subTopicsDatabase.collection("users").document(uid).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
                            @Override
                            public void onSuccess(DocumentSnapshot documentSnapshot) {

                                if (documentSnapshot.exists()) {

                                    String stName = documentSnapshot.getString("name");
                                    ArrayAdapter<String> adapter = new ArrayAdapter<>(TutorsListActivity.this, R.layout.item_subtopic, testList);
                                    adapter.notifyDataSetChanged();
                                    sListView2.setAdapter(adapter);

                                }

                                }

                            });
                        }

4 个答案:

答案 0 :(得分:2)

您断言由于Firebase的异步行为而不起作用的说法是错误的。视图未显示所需方式的原因是,每次从Firebase收到文档时,您都在更新适配器。

在伪代码中,这应该发生:

// Create function with completion block - i.e. fetchTopicNames
//
// Create array to hold fetched String values - i.e. topicNames
// For loop to request each document
//    add String value to `topicNames`
//    if current iteration is last iteration, finish forLoop and return topicNames
//

在另一种方法中,调用新创建的方法,并用topicNames的完整列表更新适配器。然后,您还可以对Array执行其他操作,例如过滤和排序。也许还有一种更有效的方法,我只是为您提供完成任务的最基本的方法。

答案 1 :(得分:2)

您需要存储元素,并且在for循环的最后中,您必须显示名称列表。

正如您所说的,您现在将获得所有UID的列表,现在您希望在列表中列出它们的名称。我已经更新了您的代码,使其正常工作。

// Create a Hashmap Object which has Key as UID and Name as Key    
HashMap<String,String> hashMap = new HashMap<>();


for (int i = 0; i<list.size(); i++) {

        final String uid = list.get(i);

        Toast.makeText(TutorsListActivity.this, uid, Toast.LENGTH_LONG).show();

        subTopicsDatabase.collection("users").document(uid).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
            @Override
            public void onSuccess(DocumentSnapshot documentSnapshot) {

                if (documentSnapshot.exists()) {

                    //Store Your UID and Name in Hashmap
                    String stName = documentSnapshot.getString("name");
                    hashMap.put(uid,stName);
                }

                //Check if it is last index of array then show the names list
                if(i==list.size()-1){
                    showListInAdapter(hashMap);
                }

            }

        });
    }

    private void showListInAdapter(HashMap<String,String> hashMap) {

        //now convert your hashmap into a list of name  and get Your Names List and show in Adapter
        ArrayList<String> listOfNames = new ArrayList<>(hashMap.keySet());

        //Set list to Adapter
        ArrayAdapter<String> adapter = new ArrayAdapter<>(TutorsListActivity.this, R.layout.item_subtopic, listOfNames);
        sListView2.setAdapter(adapter);
        adapter.notifyDataSetChanged();
    }

答案 2 :(得分:0)

我建议您遵循Guide to App Architecture并使用LiveData观察者来保持ListView的更新。您可以按照this tutorial进行操作,并将Firebase访问权限插入“存储库”类中。

更改代码以适应MVVM模式可能需要一些工作,但这也将使您的应用程序更好地运行并简化以后的开发。

答案 3 :(得分:0)

您可以通过进行递归来模拟同步获取用户(该函数将其称为self,直到索引变大,然后uid列表的大小)。

因此,首先要定义适配器和字符串列表(代表用户名)。当您这样做时,您可以调用递归,它将填充您的List和notifyDataSetChanged。这是示例

// Define empty list of user names, which you will populate later with recursion
List<String> userNames = new ArrayList<String>();
// Connect adapter with empty list
ArrayAdapter<String> adapter = new ArrayAdapter<>(TutorsListActivity.this, R.layout.item_subtopic, userNames);
// Set adapter to ListView
sListView2.setAdapter(adapter);

// Call recursion with list of uids and starting index of 0
getUserSync(list, 0);


private void getUserSync(List<String> list, int i) {
    if (i < 0 || i > list.length - 1) {
        // If index i is out of bounds for list, we break the recursion
        return;
    }

    String uid = list.get(i);

    Toast.makeText(TutorsListActivity.this, uid, Toast.LENGTH_LONG).show();

    subTopicsDatabase.collection("users").document(uid).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
        @Override
        public void onSuccess(DocumentSnapshot documentSnapshot) {
            // When we load document, we fetch name and add it to the list which is connected to adapter
            // After that, we call adapter.notifyDataSetChanged which will update ui
            // When all that is done, we call getUserSync, to fetch user name for next uid
            if (documentSnapshot.exists()) {
                String stName = documentSnapshot.getString("name");
                if (stName != null) {
                    userNames.add(stName);
                    adapter.notifyDataSetChanged();
                }
            }
            getUserSync(list, i++);
        }
    }).addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(@NonNull Exception e) {
            // If enything goes wrong, we break the recursion
            return;
        }
    });
}

如果有任何麻烦,请随时发表评论。.