首页 > 解决方案 > whereEqualTo/snapshot 监听器是否在后台线程上运行?

问题描述

我正在创建一个日记应用程序。我正在尝试获取当前用户的日记条目,然后将其转换为日记对象,该对象将被放入日记列表中。最终,这个日志列表将被发送到 RecyclerView Adapter。

我有这段代码onCreate()

myCollection.whereEqualTo("userId", userId).addSnapshotListener(new EventListener<QuerySnapshot>() {
            @Override
            public void onEvent(@Nullable @org.jetbrains.annotations.Nullable QuerySnapshot value, @Nullable @org.jetbrains.annotations.Nullable FirebaseFirestoreException error) {
                for(QueryDocumentSnapshot snapshot : value){
                    Journal myJournal = snapshot.toObject(Journal.class);
                    journals.add(myJournal);

                     
                    RecyclerViewAdapter myAdapter = new RecyclerViewAdapter(journals, JournalList.this);
                    recyclerView.setAdapter(myAdapter);
                    recyclerView.setLayoutManager(new LinearLayoutManager(JournalList.this));
                    myAdapter.notifyDataSetChanged();

                }
            }
        });

如果我移动这部分:

RecyclerViewAdapter myAdapter = new RecyclerViewAdapter(journals, JournalList.this);
                        recyclerView.setAdapter(myAdapter);
                        recyclerView.setLayoutManager(new LinearLayoutManager(JournalList.this));
                        myAdapter.notifyDataSetChanged();

onEvent块外(但仍在 onCreate() 中),日志仍被发送到 firebase,但似乎在我添加第二个帖子之前没有调用 RecyclerViewAdapter。

我的猜测是,要么 Android Studio 跳过 onEvent() 块并继续运行(考虑到它知道执行需要时间,可能会将其放入队列中),要么它在适配器部分首先完成的后台线程上运行。无论哪种方式,都会将一个空的日志数组列表发送到 Firestore。

但是,我不确定这些场景中的哪一种实际正在发生。如果有人可以确认,我将不胜感激。谢谢。

更新:不起作用的代码:

myCollection.whereEqualTo("userId", userId).addSnapshotListener(new EventListener<QuerySnapshot>() {
            @Override
            public void onEvent(@Nullable @org.jetbrains.annotations.Nullable QuerySnapshot value, @Nullable @org.jetbrains.annotations.Nullable FirebaseFirestoreException error) {
                for(QueryDocumentSnapshot snapshot : value){
                    Journal myJournal = snapshot.toObject(Journal.class);
                    journals.add(myJournal);



                }
            }
        });

        
        RecyclerViewAdapter myAdapter = new RecyclerViewAdapter(journals, JournalList.this);
        recyclerView.setAdapter(myAdapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(JournalList.this));
        myAdapter.notifyDataSetChanged();

JournalList.java 的完整代码(如果需要):

import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.firestore.CollectionReference;
import com.google.firebase.firestore.EventListener;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.FirebaseFirestoreException;
import com.google.firebase.firestore.QueryDocumentSnapshot;
import com.google.firebase.firestore.QuerySnapshot;

import java.util.ArrayList;
import java.util.List;

public class JournalList extends AppCompatActivity {

    private RecyclerView recyclerView;
    private FirebaseFirestore db = FirebaseFirestore.getInstance();
    private CollectionReference myCollection = db.collection("Journal");
    private FirebaseAuth firebaseAuth;
    private FirebaseUser currentUser;
    private List<Journal> journals;
    private Toolbar myToolbar;

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

        recyclerView = findViewById(R.id.recyclerView);

        firebaseAuth = FirebaseAuth.getInstance();
        currentUser = firebaseAuth.getCurrentUser();
        String userId = currentUser.getUid();
        myToolbar = findViewById(R.id.my_toolbar);
        journals = new ArrayList<>();

        setSupportActionBar(myToolbar);




        myCollection.whereEqualTo("userId", userId).addSnapshotListener(new EventListener<QuerySnapshot>() {
            @Override
            public void onEvent(@Nullable @org.jetbrains.annotations.Nullable QuerySnapshot value, @Nullable @org.jetbrains.annotations.Nullable FirebaseFirestoreException error) {
                for(QueryDocumentSnapshot snapshot : value){
                    Journal myJournal = snapshot.toObject(Journal.class);
                    journals.add(myJournal);



                }
            }
        });

        //my guess:
        RecyclerViewAdapter myAdapter = new RecyclerViewAdapter(journals, JournalList.this);
        recyclerView.setAdapter(myAdapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(JournalList.this));
        myAdapter.notifyDataSetChanged();










    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        getMenuInflater().inflate(R.menu.menu, menu);

        return super.onCreateOptionsMenu(menu);



    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()){
            case R.id.addNote:

                startActivity(new Intent(JournalList.this, PostJournalActivity.class));

                break;
            case R.id.sign_out:

                firebaseAuth.signOut();
                startActivity(new Intent(JournalList.this, MainActivity.class));

                break;
        }

        return super.onOptionsItemSelected(item);


    }

    @Override
    protected void onDestroy() {

        super.onDestroy();
    }
}

标签: javaandroidfirebasegoogle-cloud-firestore

解决方案


网络请求等在后台线程上运行,但回调没有。

但是我认为您在这里的误解来自回调的工作方式。当您在EventListener此处传递时,您传递的是一个覆盖该onEvent方法的类(在本例中为匿名类)。正如您提到的,没有什么可以“跳过”的。

让我们考虑一个例子。假设我定义了一个接口Callback

interface Callback {

    void myMethod();
}

然后我可以写一个冗余的方法:

void doSomethingWith(Callback myCallback) {

    myCallback.myMethod();
}

现在在这里,当我打电话时

doSomethingWith(new Callback() {
    @Override
    public void myMethod() {
        
        // ...
    }
});

doSomethingElse();

执行前myMethod运行doSomethingElse

但是,它不必是这种方式。假设我改为doSomethingWith这样定义:

void doSomethingWith(Callback myCallback) {

    // Do nothing
}

然后你的实现中的代码myMethod永远不会被调用。仅仅因为您传入了一个实现方法的类,并不能保证它何时会被调用或它是否会被调用。

为了把它带回 Firebase,我们可以考虑一个这样的例子:

void doSomethingWith(Callback myCallback) {

    // Switch to a background thread,
    // wait for a network request then call myCallback.myMethod()
}

所以在这里你的回调被调用,但它会在稍后的时间,当网络请求完成时,与你的 Firebase 监听器完全相同的场景。


现在让我们实际解决您的问题。(对我来说)最有意义的是首先声明适配器,然后在onEvent调用时更新数据:

RecyclerViewAdapter myAdapter = new RecyclerViewAdapter(journals, JournalList.this);
recyclerView.setAdapter(myAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(JournalList.this));

myCollection...addSnapshotListener(new EventListener<QuerySnapshot>() {
    @Override
    public void onEvent(...) {

        myAdapter.data = ...
        myAdapter.notifyDataSetChanged();
    }
});

附带说明一下,Android Studio 绝对不会在这里发挥作用,它是一个 IDE。不要将 Android 与 Android Studio 混淆。


推荐阅读