![](/img/trans.png)
[英]Firestore security rules: check a condition for every value in an array
[英]A condition depend on a variable value that I want to change value in onCompleteListenr of Firestore but that listener change value after if condition
我有一个需要 email 和密码的表格。 因此,在首先输入数据后,我检查了 email 是否已经在文档中可用(在 Firebase firestore 数据库中)。 如果不可用,那么我将插入这些数据。 但是我的代码总是先插入,然后再执行对已存在内容的检查。 有什么方法可以解决它而无需在检查代码中使用我的插入代码。
例如,我的数据库中已有“test@gmail.com”文件。 当我输入相同的 email 并提交时,它将显示帐户可用。 无法注册。 但是我的代码再次插入,然后检查数据库中的 email 可用性。
感谢您分享宝贵的时间。
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!");
}
}
});
}
}
数据是从 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
尚未执行。
此类问题的解决方案总是相同的:任何需要数据库数据的代码都必须在onComplete
内部,从那里调用,或者以其他方式同步。
最简单的修复是将第二个读取操作移到第一个的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 调用是一个非常常见的问题,所以我建议阅读它。 一些参考:
这些调用是异步的,所以你放在onComplete
中的代码不会按顺序运行——它会在未来某个时间当数据可用时运行,你必须相应地构建你的代码。 您不能只写一个很长的 function 并期望它像使用同步方法一样工作。
有很多方法可以解决这个问题,但所有这些方法都需要您了解回调代码(无论是您自己的自定义回调还是传递给 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);
}
});
您调用的第一个 function 发出第一个异步请求的地方
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
中调用下一个 function。 在检索到数据之前您不能调用它,并且在调用您的侦听器之前不会发生这种情况。
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!");
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.