首先,如果标题误导我,我深表歉意。英语不是我的母语,所以我不确定该如何命名。现在的问题:
我有一个活动,可显示存储在Firebase项目中有关用户的数据。数据在Firebase用户(显示名称,电子邮件和个人资料图像)和Cloud Firestore中名为用户UID
的文档之间分配。
此活动开始时,我进行了Firebase谷歌身份验证以获取用户,然后出现了问题。我需要知道用户是否在数据库中有一个链接文档及其附加数据(现有用户),或者是否需要创建一个链接文档(新用户)。我创建了一种方法来检查是否存在像用户的UID这样的名称的文档。这是方法:
public void userExists(String uid) {
FirebaseFirestore db = FirebaseFirestore.getInstance();
DocumentReference docRef = db.collection("users").document(uid);
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
@Override
public void onComplete(@NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot documentSnapshot = task.getResult();
if (documentSnapshot.exists()) {
aa = true;
aa=true;
} else {
aa = false;
}
} else {
aa = false;
}
}
});
}
({aa
是在活动中声明的布尔变量)。
我在下一个方法中调用此方法,以了解是否需要启动新的Activity来创建文档,或者是否可以在当前Activity中显示用户的数据而没有问题。
private void updateUI(FirebaseUser user) {
if (user != null) {
userExists(user.getUid());
if(aa){
//Fill layout with the user data and the user linked document data
//USER DATA
txvNombre=findViewById(R.id.nombrePerfil);
txvNombre.setText(user.getDisplayName());
imvAvatar=findViewById(R.id.imvVistaPerfilAvatar);
Picasso.with(VistaPerfilActivity.this)
.load(user.getPhotoUrl())
.resize(500,500)
.centerCrop()
.into(imvAvatar);
//HERE GOES THE DOCUMENT DATA
}else{
}
} else {
finish();
}
}
据我所知,Firestore连接是在新线程中建立的,因此,当UpdateUI(FirebaseUser user)启动时,aa
始终为false,因为userExists(String uid)尚未完成。 userExists(String uid)正常工作,我已经检查了它。
所以我需要知道如何检查Firestore连接线程是否完成,以便继续执行该应用程序。我已经尝试使用OnCompleteListener(如代码所示),但是它不起作用。我还尝试过在userExists(String uid)方法中编写操作,而不是仅更改aa的值,然后继续使用另一种方法,但是我得到了
从内部类内部访问变量需要声明为最终变量
错误。我尝试遵循Android Studio的建议,将变量定为final,但是出于明显的原因,我无法使用它。
谢谢。
答案 0 :(得分:1)
问题不是由多线程引起的,而是由于从Firebase异步加载数据这一事实。到您的updateUI
函数查看aa
的值时,onComplete
尚未运行。
最简单的方法是放置一些放置良好的日志记录语句:
System.out.println("Before attaching listener");
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
@Override
public void onComplete(@NonNull Task<DocumentSnapshot> task) {
System.out.println("Got document");
}
});
System.out.println("After attaching listener");
运行此代码时,它将打印
在附加侦听器之前
附加监听器后
获得文档
这可能不是您所期望的,但恰恰说明了aa
检查updateUI
时为什么未修改的原因。尚未从Firestore中读取该文档,因此onComplete
尚未运行。
解决方案是将所有需要数据的代码从数据库移入到onComplete
方法中。这种情况下最简单的方法是:
public void userExists(String uid) {
FirebaseFirestore db = FirebaseFirestore.getInstance();
DocumentReference docRef = db.collection("users").document(uid);
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
@Override
public void onComplete(@NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot documentSnapshot = task.getResult();
if (documentSnapshot.exists()) {
//Fill layout with the user data and the user linked document data
//USER DATA
txvNombre=findViewById(R.id.nombrePerfil);
txvNombre.setText(user.getDisplayName());
imvAvatar=findViewById(R.id.imvVistaPerfilAvatar);
Picasso.with(VistaPerfilActivity.this)
.load(user.getPhotoUrl())
.resize(500,500)
.centerCrop()
.into(imvAvatar);
//HERE GOES THE DOCUMENT DATA
}
}
}
});
}
现在,需要文档的代码仅在文档实际可用后才能运行。这将起作用,但是确实会使userExists
函数的可重用性降低。如果要解决此问题,可以将回调传入 userExists
,然后在加载文档后调用该回调。
public interface UserExistsCallback {
void onCallback(boolean isExisting);
}
并在userExists
中将其用作:
public void userExists(String uid, final UserExistsCallback callback) {
FirebaseFirestore db = FirebaseFirestore.getInstance();
DocumentReference docRef = db.collection("users").document(uid);
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
@Override
public void onComplete(@NonNull Task<DocumentSnapshot> task) {
boolean userExists = false;
if (task.isSuccessful()) {
DocumentSnapshot documentSnapshot = task.getResult();
userExists = documentSnapshot.exists();
}
callback.onCallback(userExists);
}
});
}
然后使用以下命令从updateUI
中调用它:
if (user != null) {
userExists(user.getUid(), new UserExistsCallback() {
public void onCallback(boolean isExisting) {
if(isExisting){
//Fill layout with the user data and the user linked document data
//USER DATA
txvNombre=findViewById(R.id.nombrePerfil);
txvNombre.setText(user.getDisplayName());
imvAvatar=findViewById(R.id.imvVistaPerfilAvatar);
Picasso.with(VistaPerfilActivity.this)
.load(user.getPhotoUrl())
.resize(500,500)
.centerCrop()
.into(imvAvatar);
//HERE GOES THE DOCUMENT DATA
}else{
}
} else {
finish();
}
});
}
您可以看到我们的``回调与Firestore本身的OnCompleteListener
非常相似,它只是更适合我们的需求。
这个问题经常出现,所以我建议花一些时间来学习更多有关它的信息。参见:
答案 1 :(得分:0)
关于最终引用,如果变量属于类而不是在方法中声明,则无需声明为最终变量。
答案 2 :(得分:0)