从firebase获取数据并将其返回到主线程[重复]

Posted

技术标签:

【中文标题】从firebase获取数据并将其返回到主线程[重复]【英文标题】:fetching data from firebase and returning it to the main thread [duplicate] 【发布时间】:2020-05-28 09:25:39 【问题描述】:

目前遇到我从 firebase 获取数据的问题。我知道这是因为 Firebase 是异步的,所以当我进行 firebase 调用时,它会在自己的线程上执行,并且调用它的当前线程会继续执行。我正在使用来自 firebase 的数据填充对象列表,然后返回该对象列表。问题是,列表总是返回 null,因为 firebase 代码的执行没有及时完成。

我创建了一些从 SQLite db 中获取的异步代码,效果很好,但这种方法似乎不适用于 firebase(我相信这是因为 firebases API 是异步的)这是我从 firebase 返回对象列表的方法.

/** Method to get activity data from firebase.
 * @param userInput the user query to select the data
 * @return a list of activity models based on the query
 * */
public List<ActivityModel> retrieveActivityData(String userInput) 
    Log.d(TAG, "retrieveActivityData: starts");
    List<ActivityModel> models = new ArrayList<ActivityModel>();
    // execute the query in firebase
    CollectionReference activfitCollection = db.collection("activity");
    activfitCollection.orderBy("isoTimestamp")
            .startAt(userInput)
            .endAt(DateHelper.getDayEndingDate(userInput))
            .get()
            .addOnCompleteListener(task -> 
                if (task.isSuccessful()) 
                    Log.d(TAG, "onComplete: Getting data successful!");
                    // check to see if it exists
                    if (!task.getResult().isEmpty()) 
                        for (DocumentSnapshot documentSnapshot : task.getResult().getDocuments()) 
                            Log.d(TAG, "retrieveActivityData: document = " + documentSnapshot.getId());
                            // cast the document to the activity model
                            Log.d(TAG, "retrieveActivityData: document data " + documentSnapshot.getData());
                            ActivityModel model = mapToActivityModel(documentSnapshot);
                            models.add(model);
                            Log.d(TAG, "retrieveActivityData: array size" + models.size());
                        
                    

                 else 
                    Log.e(TAG, "onComplete: Error getting documents: ", task.getException());
                
            );
    Log.d(TAG, "retrieveActivityData: array size outside " + models.size());
    return models;

【问题讨论】:

【参考方案1】:

选项 - 1:您可以使用LiveData 来实现此目的。操作完成后将值发布到 LiveData 并观察您的活动或片段中的内容

MutableLiveData<List<ActivityModel>> listMutableLiveData = new MutableLiveData<>();

public MutableLiveData<List<ActivityModel>> retrieveActivityData(String userInput) 

    List<ActivityModel> models = new ArrayList<ActivityModel>();

    ....
        if (task.isSuccessful()) 
            for (DocumentSnapshot documentSnapshot : task.getResult().getDocuments()) 
                ....
                models.add(model);
            

            //Post value to live data from here
            listMutableLiveData.postValue(models);
        

    ....

    return listMutableLiveData;

然后像这样observe

retrieveActivityData(userInput).observe(this, new Observer<List<ActivityModel>>() 
    @Override
    public void onChanged(List<ActivityModel> activityModels) 
        //you can use list here
    
);

选项 - 2:您可以在firebase操作完成时使用回调函数获取结果。

为回调创建interface
interface FirebaseResultListener 
    void onComplete(List<ActivityModel> activityModels);

配置您的 retrieveActivityData 以处理此回调
public void retrieveActivityData(String userInput, FirebaseResultListener callback) 

    List<ActivityModel> models = new ArrayList<ActivityModel>();

    ....
    if (task.isSuccessful()) 
        for (DocumentSnapshot documentSnapshot : task.getResult().getDocuments()) 
                ....
            models.add(model);
        

        //Invoke callback with result from here
        callback.onComplete(models);
    

    ....

在您的活动或片段中实现此接口
retrieveActivityData(userInput, new FirebaseResultListener() 
    @Override
    public void onComplete(List<ActivityModel> activityModels) 
        //you can use list here
    
);

【讨论】:

太棒了,感谢您的反馈和回复。最初,我使用 AsyncTask 在单独的线程上执行此方法。我创建了自己的自定义回调方法,该方法应该从 firebase 返回对象列表。但我会检查这些解决方案,看看哪些最适合! 问题,如果我不想使用片段/活动中的数据怎么办?例如,我不会使用从 firebase 获取的数据来更新 UI。我只想将从firebase获取的数据存储在内存中并确保它等待该数据加载到内存中,我应该让该特定类实现回调接口还是必须是实现回调接口的活动或片段? 您可以从任何地方使用回调方法。如果这对您有帮助,请接受答案 @FanonX,请接受未来用户的答案,以便他们发现它有帮助【参考方案2】:

由于数据是异步加载的,无法返回数据,需要在retrieveActivityData方法中传入callback(Interface),并使用interface的callback来加载数据,查看以下代码

  @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);

        MyFirebaseCallback myFirebaseCallback = new MyFirebaseCallback() 
            @Override
            public void dataLoaded(List<ActivityModel> activityModels) 
                //set the data in recycler view
            
        ;

        retrieveActivityData("myInput",myFirebaseCallback);
    

    public void retrieveActivityData(String userInput, final MyFirebaseCallback callback) 
        Log.d(TAG, "retrieveActivityData: starts");
        // execute the query in firebase
        CollectionReference activfitCollection = db.collection("activity");
        activfitCollection.orderBy("isoTimestamp")
                .startAt(userInput)
                .endAt(DateHelper.getDayEndingDate(userInput))
                .get()
                .addOnCompleteListener(task -> 
                    if (task.isSuccessful()) 
                        List<ActivityModel> models = new ArrayList<ActivityModel>();
                        Log.d(TAG, "onComplete: Getting data successful!");
                        // check to see if it exists
                        if (!task.getResult().isEmpty()) 
                            for (DocumentSnapshot documentSnapshot : task.getResult().getDocuments()) 
                                Log.d(TAG, "retrieveActivityData: document = " + documentSnapshot.getId());
                                // cast the document to the activity model
                                Log.d(TAG, "retrieveActivityData: document data " + documentSnapshot.getData());
                                ActivityModel model = mapToActivityModel(documentSnapshot);
                                models.add(model);
                                Log.d(TAG, "retrieveActivityData: array size" + models.size());
                            
                        

                        callback.dataLoaded(models);

                     else 
                        Log.e(TAG, "onComplete: Error getting documents: ", task.getException());
                    
                );
        Log.d(TAG, "retrieveActivityData: array size outside " + models.size());
    

    interface MyFirebaseCallback
        void dataLoaded(List<ActivityModel> activityModels);
    

【讨论】:

太棒了,非常感谢您的反馈,就我而言,最好的方法似乎是回调方法。【参考方案3】:

你猜对了!该查询是异步的,因此retrieveActivityData() 不应返回List&lt;ActivityModel&gt;,否则它将始终为空。一旦您的ListonComplete() 中编译,您就必须使用事件总线来触发事件,或者使用LiveData 并观察它。

LiveData

ViewModel

EventBus

【讨论】:

以上是关于从firebase获取数据并将其返回到主线程[重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Firebase Firestore 获取数据并将其存储在列表中以在颤振小部件中使用?

从 firebase 检索数据并将其显示在可扩展的 recyclerview 中

从firebase获取数据时Recyclerview为空白

将 Firebase Firestore 文档值分配给 swift 变量 [重复]

尝试获取 Firebase 云数据库数据并将其显示在 RecyclerView 中的片段中

如何将数据从AsyncTask返回到主线程