firebase 一个条件依赖于一个变量值,我想在Firestore的onCompleteListenr中更改该值,但侦听器在if条件之后更改了值

3mpgtkmj  于 2023-01-02  发布在  其他
关注(0)|答案(2)|浏览(180)

我有一个表单,需要电子邮件和密码。所以在输入数据后,我先检查电子邮件是否已在文档中可用(在Firebase Firestore数据库中)。如果不可用,我将插入这些数据。但我的代码总是先插入,然后再执行检查已经存在的内容。有没有任何方法可以解决它,而不使用我的插入代码内的检查代码。
For example, my database already has "test@gmail.com" document. When I input the same email and submit then it will show Account available. Can not sign up! But my code inserts again then check for the email availability in DB.
感谢您抽出宝贵的时间。
XML代码:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical"
    android:paddingTop="30dp">

    <View
        android:layout_width="400dp"
        android:layout_height="1dp"
        android:background="#678049"
        android:layout_gravity="center"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="30dp"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center"
        android:layout_marginTop="5dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Email"
            android:textSize="20dp"
            android:textColor="@color/black"
            android:layout_marginLeft="5dp"
            android:layout_gravity="center|top"/>

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center"
        android:layout_marginTop="5dp">

        <EditText
            android:id="@+id/UserEmail"
            android:layout_width="345dp"
            android:layout_height="40dp"
            android:textSize="20dp"
            android:background="#5676"
            android:hint="Enter  Email Adress.."
            android:inputType="text"
            android:layout_marginLeft="3dp"/>

    </LinearLayout>
    <Space
        android:layout_width="match_parent"
        android:layout_height="5dp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center"
        android:layout_marginTop="5dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Password"
            android:textColor="@color/black"
            android:textSize="20dp"
            android:layout_marginLeft="5dp" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center"
        android:layout_marginTop="5dp">

        <EditText
            android:id="@+id/UserPassword"
            android:layout_width="345dp"
            android:layout_height="40dp"
            android:textSize="20dp"
            android:background="#5676"
            android:hint="Enter Password.."
            android:layout_marginVertical="13dp"
            android:inputType="textPassword" />

    </LinearLayout>

    <Button
        android:id="@+id/btnSubmit"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:padding="10dp"
        android:layout_marginHorizontal="80dp"
        android:backgroundTint="#4C4B4B"
        android:text="Sign up"
        android:textColor="#F5FBF6" />

</LinearLayout>

Java代码

public class MainActivity extends AppCompatActivity {
    public static final String COLLECTION_USER = "collection_user";
    EditText userEmail, userPassword;
    Button btnSignUp;
    boolean accountAlreadyAvailable = false;

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

        userEmail = findViewById(R.id.UserEmail);
        userPassword = findViewById(R.id.UserPassword);
        btnSignUp = findViewById(R.id.btnSubmit);

        btnSignUp.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String email = userEmail.getText().toString();
                String password = userPassword.getText().toString();

                Map<String, Object> data = new HashMap<>();
                data.put("email", email);
                data.put("password", password);

                FirebaseFirestore db = FirebaseFirestore.getInstance();
                db.collection(COLLECTION_USER).document(email)
                        .get()
                        .addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
                            @Override
                            public void onComplete(@NonNull Task<DocumentSnapshot> task) {
                                if(task.getResult().exists()) {
                                    accountAlreadyAvailable = true;
                                    Log.d(TAG, "Account available changed to: " + accountAlreadyAvailable);
                                }
                            }
                        });

                //As no email document found,
                //new data will insert to firebase firestore database
                Log.d(TAG, "Account available: " + accountAlreadyAvailable);
                if(accountAlreadyAvailable == false) {
                    db.collection(COLLECTION_USER).document(email).set(data).addOnCompleteListener(new OnCompleteListener<Void>() {
                        @Override
                        public void onComplete(@NonNull Task<Void> task) {
                            if(task.isSuccessful()) {
                                Log.d(TAG, "Data inserted as no account found");
                            }
                        }
                    });
                } else {
                    Log.d(TAG, "Account available. Can not sign up!");
                }
            }
        });
    }
}
fdbelqdn

fdbelqdn1#

数据是从Firestore(以及大多数现代云API)异步加载的,这会改变代码执行的顺序,而不是您可能习惯的顺序。通过设置断点并在调试器中运行,或者添加一些日志记录,最容易看到这一点:

Log.i("Firestore", "Before calling get()");
FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection(COLLECTION_USER).document(email)
.get()
.addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
    @Override
    public void onComplete(@NonNull Task<DocumentSnapshot> task) {
        Log.i("Firestore", "Got data");
    }
});
Log.i("Firestore", "After calling get()");

当您运行此代码时,它会记录:
调用get()之前
调用get()后
获取数据
这可能不是您预期的代码执行顺序,但它按设计工作,并完美地解释了为什么第二次读取数据库时不起作用:到运行accountAlreadyAvailable = true的时候还没有执行。
此类问题的解决方案始终相同:需要来自数据库的数据的任何代码必须在X1 M1 N1 X内部,从那里被调用,或者以其他方式被同步。
最简单的修复方法是将第二个读取操作移到第一个的onComplete中:

FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection(COLLECTION_USER).document(email)
.get()
.addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
    @Override
    public void onComplete(@NonNull Task<DocumentSnapshot> task) {
        if(task.getResult().exists()) {
            accountAlreadyAvailable = true;
            Log.d(TAG, "Account available changed to: " + accountAlreadyAvailable);
        }

        // 👇
        if(accountAlreadyAvailable == false) {
            db.collection(COLLECTION_USER).document(email).set(data).addOnCompleteListener(new OnCompleteListener<Void>() {
                @Override
                public void onComplete(@NonNull Task<Void> task) {
                    if(task.isSuccessful()) {
                        Log.d(TAG, "Data inserted as no account found");
                    }
                }
            });
        } else {
            Log.d(TAG, "Account available. Can not sign up!");
        }
    }
});

现在,第二次读取仅在第一次读取完成且accountAlreadyAvailable已设置后执行。
处理异步API调用是一个非常常见的问题,因此我建议您仔细阅读它。

  • Android studio Firebase查询-从回调函数中检索值并将其分配给变量
  • getContactsFromFirebase()方法返回空列表
  • 为什么调用API或启动协程的函数返回空值?
  • Setting Singleton property value in Firebase Listener
  • Firestore Android Java获取外部类中已修改变量的值
sqougxex

sqougxex2#

这些调用是异步的,所以你放入onComplete中的代码不会按顺序运行--它会在将来某个时候运行,当数据可用时,你必须相应地构造你的代码。你不能只写一个长函数,然后期望它像使用同步方法一样工作。
有很多方法可以解决这个问题,但是所有的方法都要求你理解回调代码(无论是你自己的自定义回调还是传递给Firestore的回调)不会立即运行。自定义回调很少有必要作为一个解决方案- Firestore * 已经 * 使用了回调。相反,你只需要理解回调是如何工作的,并正确地使用它。
例如,您可以通过将工作分解为几个功能来解决问题:

btnSignUp.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        String email = userEmail.getText().toString();
        String password = userPassword.getText().toString();
        createAccount(email, password);
    }
});

其中调用的第一个函数发出第一个异步请求

private void createAccount(String email, String password) {
    Map<String, Object> data = new HashMap<>();
    data.put("email", email);
    data.put("password", password);

    FirebaseFirestore db = FirebaseFirestore.getInstance();
    db.collection(COLLECTION_USER).document(email)
            .get()
            .addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
                @Override
                public void onComplete(@NonNull Task<DocumentSnapshot> task) {
                    // The "createAccount" function will exit before this is run,
                    // so this MUST be called inside onComplete
                    finishSignUp(task.getResult().exists());
                }
            });
}

然后调用onComplete内部的下一个函数,直到数据被获取,你不能调用这个函数,并且直到你的监听器被调用。

private void finishSignUp(boolean accountAvailable) {
    //As no email document found,
    //new data will insert to firebase firestore database
    Log.d(TAG, "Account available: " + accountAvailable);

    FirebaseFirestore db = FirebaseFirestore.getInstance();
    if(!accountAvailable) {
        db.collection(COLLECTION_USER).document(email).set(data).addOnCompleteListener(new OnCompleteListener<Void>() {
            @Override
            public void onComplete(@NonNull Task<Void> task) {
                if(task.isSuccessful()) {
                    Log.d(TAG, "Data inserted as no account found");
                }
            }
        });
    } else {
        Log.d(TAG, "Account available. Can not sign up!");
    }
}

相关问题