Firestore连接执行线程时出现问题 [英] Problems with Firestore connections executing threads

查看:84
本文介绍了Firestore连接执行线程时出现问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,如果标题误导我,我深表歉意.英语不是我的母语,所以我不确定该如何命名.现在的问题:

First of all, I would like to apologize if the title is misleading. English is not my native language and I wasn't sure how to name this post. Now the question:

我有一个活动,可显示存储在Firebase项目中有关用户的数据.数据在Firebase用户(显示名称,电子邮件和个人资料图像)和Cloud Firestore中名为该用户UID的文档之间分配.

I have an Activity that shows the data stored in a Firebase project about a user. The data is distributed between the Firebase user (display name, email and profile image) and a document in Cloud Firestore named as the user's UID.

此活动开始时,我进行了Firebase谷歌身份验证以获取用户,然后出现了问题.我需要知道用户是否在数据库中有一个链接文档及其附加数据(现有用户),或者他是否需要创建一个链接文档(新用户).我创建了一种方法来检查是否存在像用户的UID这样的名称的文档.这是方法:

When this activity starts, I make a Firebase google auth to get the user, and then the problems come. I need to know if the user has a linked document in the database with his additional data (existing user) or if he needs to create one (new user). I have created a method that checks if a document named like the user's UID exists. This is the method:

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是在活动中声明的布尔变量).

(aa is a boolean variable declared in the Activity).

我在下一个方法中调用此方法,以了解是否需要启动新的Activity以创建文档,或者是否可以在当前Activity中显示用户的数据而没有问题.

I call this method inside the following one in order to know if I need to start a new Activity to create the document or if I can show the user's data in the current Activity without problems.

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)正常工作,我已经检查了.

As far as I know, Firestore connections are made in a new Thread so, when UpdateUI(FirebaseUser user) starts, aa is always false, because userExists(String uid) hasn't finished yet. userExists(String uid) works correctly, I have checked it.

所以我需要知道如何检查Firestore连接线程是否完成,以便继续执行该应用程序.我尝试使用OnCompleteListener(如代码中所示),但是它不起作用.我还尝试过在userExists(String uid)方法中编写操作,而不是仅更改aa的值,然后继续使用另一种方法,但是我得到了

So I need to know how to check if the Firestore connection thread is finished, in order to continue executing the app. I have tried using the OnCompleteListener (shown in the code), but it doesn't work. I've also tried to just write the actions in the userExists(String uid) method instead of just changing the value of aa and then continue on another method, but I get the

从内部类内部访问

变量需要声明为最终变量

variable is accessed from within inner class needs to be declared final

错误.我尝试遵循Android Studio的建议,将变量定为final,但是出于明显的原因,我无法使用它.

error. I tried to follow the Android Studio advice of making the variable final, but I can't work with that for obvious reasons.

谢谢.

推荐答案

问题不是由多线程引起的,而是由Firebase异步加载数据引起的.当您的updateUI函数查看aa的值时,onComplete尚未运行.

The problem is not so much caused by multi-thread, as by the fact that data is loaded from Firebase asynchronously. By the time your updateUI function looks at the value of aa, the onComplete hasn't run yet.

通过放置一些放置良好的日志记录语句,最容易看出这一点:

This is easiest to see by placing a few well placed logging statements:

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");

运行此代码时,它将打印

When you run this code it prints

在附加侦听器之前

Before attaching listener

附加监听器后

获得文档

这可能不是您所期望的,但恰恰说明了为什么updateUI检查时未修改aa的原因.尚未从Firestore中读取该文档,因此onComplete尚未运行.

This is probably not what you expected, but it explains precisely why aa is unmodified when updateUI checks it. The document hasn't been read from Firestore yet, so onComplete hasn't run yet.

解决方案是将所有需要数据的代码从数据库移入 onComplete方法.在您的情况下,最简单的方法是:

The solution for this is to move all code that requires data from the database into the onComplete method. The simplest way in your case is:

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,然后在文档加载后调用.

Now your code that needs the document only runs after the document is actually available. This will work, but it does make the userExists function a bit less reusable. If you want to fix that, you can pass a callback into userExists that you then call after the document is loaded.

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调用它:

And then invoke that from updateUI with:

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非常相似,它只是更符合我们的需求.

As you can see our `` callback is quite similar to the OnCompleteListener of Firestore itself, it's just a bit more tailored to our needs.

这个问题经常出现,所以我建议花一些时间来学习更多有关它的信息.参见:

This problem pops up a lot, so I recommend spending some time learning more about it. See:

  • get all table values from firebase null object reference firebase database
  • Setting Singleton property value in Firebase Listener

这篇关于Firestore连接执行线程时出现问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆