java - 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();
}
}
解决方案
网络请求等在后台线程上运行,但回调没有。
但是我认为您在这里的误解来自回调的工作方式。当您在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 混淆。
推荐阅读
- ios - 将带有列表的地图添加到移动应用程序的表单?
- angular - 如何在 Angular 中显示未授权访问的错误页面?
- authorization - 如何创建 XACML 策略并请求单个用户在相同资源或不同资源中具有多个角色
- python - 在 Python 中使用 PIL 调整图像大小时,'int' 对象不可下标
- c - 使用二维数组参数编译函数时出现 NVCC 错误
- c# - 无法从 ThrowIfCancellationRequested() 捕获异常
- python - 是否可以连接保存在 Box 文件夹中的 access 数据库?
- reactjs - 使用连接的组件和 redux 进行测试 #1262
- php - 如何循环最后插入 id
- laravel-backpack - Laravel 背包限制用户访问管理面板