Android Firebase Auth注册问题:布尔变量赋值失败及Firestore写入异常
看起来你遇到的核心问题都是Firebase异步操作的特性导致的——Firestore的查询和Auth的创建用户都是后台异步任务,你的同步方法会在回调执行前就返回初始值,同时保存用户的逻辑也因为时机不对没触发。下面一步步拆解解决方案:
一、为什么布尔变量赋值不正确?
Firebase的get()和createUserWithEmailAndPassword都是异步执行的:方法调用后会立即返回,而真正的查询/认证结果要等到后台任务完成后才会在addOnCompleteListener里执行。你的checkUsernameIsExist和createUserWithEmailAndPassword方法在回调还没跑的时候就返回了isUsernameTaken/isMailAuthenticationSuccess,这时候这些变量还是初始值(默认false),自然得不到正确结果。
二、重构异步逻辑:用回调接口替代同步返回
我们需要把同步返回布尔值的方式改成回调接口,让异步任务完成后再触发后续逻辑:
1. 重构用户名检查方法
首先定义一个回调接口,然后修改checkUsernameIsExist方法,去掉返回值,改用回调传递结果:
// 定义用户名检查的回调接口 interface UsernameCheckCallback { void onUsernameCheckResult(boolean isTaken); } private void checkUsernameIsExist(final String usernameRegister, UsernameCheckCallback callback) { FirebaseFirestore rootRef = FirebaseFirestore.getInstance(); DocumentReference docIdRef = rootRef.collection("users").document(usernameRegister); docIdRef.get().addOnCompleteListener(task -> { if (task.isSuccessful()) { DocumentSnapshot document = task.getResult(); boolean isTaken = document.exists(); if (isTaken) { Log.d(TAG, "Document exists!"); Toast.makeText(Register.this, "User name already taken", Toast.LENGTH_LONG).show(); } else { Log.d(TAG, "Document does not exist!"); Toast.makeText(Register.this, "Username is available", Toast.LENGTH_LONG).show(); } // 任务完成后通过回调返回结果 callback.onUsernameCheckResult(isTaken); } else { Log.d(TAG, "Failed with: ", task.getException()); // 错误情况下也返回结果(比如默认false) callback.onUsernameCheckResult(false); } }); }
2. 重构邮箱认证方法
同样用回调处理认证结果:
// 定义认证回调接口 interface AuthCallback { void onAuthSuccess(User newUser); void onAuthFailure(String errorMessage); } private void createUserWithEmailAndPassword(final String nameRegister, final String surnameRegister, final String usernameRegister, final String emailRegister, String passwordRegister, AuthCallback callback) { auth.createUserWithEmailAndPassword(emailRegister, passwordRegister) .addOnCompleteListener(Register.this, task -> { if (!task.isSuccessful()) { String errorMsg = "Mail is already taken"; email.setError(errorMsg); email.requestFocus(); callback.onAuthFailure(errorMsg); } else { User newUser = new User(nameRegister, surnameRegister, usernameRegister, emailRegister); auth.signOut(); // 认证成功后传递创建好的User对象 callback.onAuthSuccess(newUser); } }); }
三、修改注册主逻辑,链式处理异步流程
现在把原来的同步逻辑改成异步链式调用,确保每一步都在前一步完成后执行:
// 初始的非空校验 if(!nameRegister.isEmpty() && !surnameRegister.isEmpty() && !usernameRegister.isEmpty() && !emailRegister.isEmpty() && !passwordRegister.isEmpty() && !passwordConfirmRegister.isEmpty()){ if (passwordRegister.equals(passwordConfirmRegister)) { // 第一步:检查用户名是否可用 checkUsernameIsExist(usernameRegister, isTaken -> { if (!isTaken) { // 第二步:用户名可用,执行邮箱认证 createUserWithEmailAndPassword(nameRegister, surnameRegister, usernameRegister, emailRegister, passwordRegister, new AuthCallback() { @Override public void onAuthSuccess(User newUser) { // 第三步:认证成功,保存用户到Firestore saveUserToFirebase(newUser); // 跳转登录页 startActivity(new Intent(Register.this, Login.class)); finish(); } @Override public void onAuthFailure(String errorMessage) { // 处理认证失败的情况(比如提示用户) } }); } }); } else { passwordConfirm.setError("Passwords do not match."); passwordConfirm.requestFocus(); } }
四、为什么之前无法写入Firebase?
原来的代码里if(isMailAuthenticationSuccess) saveUserToFirebase(newUser);会在createUserWithEmailAndPassword方法返回后立即执行,但此时认证的异步任务还没完成,isMailAuthenticationSuccess还是初始值(false),而且newUser也还没被创建,所以这个判断永远不会触发,自然不会写入数据。
现在把saveUserToFirebase放在认证成功的回调里,就能确保:
newUser已经被正确初始化- 只有认证成功后才会执行保存操作
额外注意事项
- 去掉原来的
isUsernameTaken、isMailAuthenticationSuccess、newUser这些全局变量,改用回调传递数据,避免全局变量的线程安全问题 - 确保
User类有正确的getUsername()方法,因为你用它作为Firestore文档的ID - 可以考虑给
saveUserToFirebase也添加回调,处理保存成功/失败的情况
内容的提问来源于stack exchange,提问作者ccanozerr

