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

查看:17
本文介绍了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 google auth 来获取用户,然后问题就来了.我需要知道用户在数据库中是否有一个带有附加数据(现有用户)的链接文档,或者他是否需要创建一个(新用户).我创建了一个方法来检查是否存在一个名为用户 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是Activity中声明的布尔变量).

(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

从内部类访问的变量需要声明为final

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

附加监听器之前

附加监听器后

得到文件

这可能不是您所期望的,但它准确地解释了为什么 aaupdateUI 检查时未被修改.尚未从 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 调用它:

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();
    }
  });
}

如您所见,我们的 UserExistsCallback 与 Firestore 本身的 OnCompleteListener 非常相似,只是更适合我们的需求.

As you can see our UserExistsCallback 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:

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

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