Firebase Firestore SnapshotListener无限触发原因求助
嘿,我来帮你搞定这个无限触发的问题!
你的SnapshotListener之所以会无限触发,核心原因就是循环触发的闭环:
- 当你调用
addSnapshotListener后,只要目标文档的数据发生变化,监听器的onEvent方法就会自动执行 - 但你在
onEvent里又调用了documentReference.set(userDataX),这会直接修改该文档的数据 - 文档一更新,监听器又被触发,再次执行更新操作,周而复始就形成了无限循环!
两种解决方案任你选
方案1:改用一次性读取+更新(推荐,适合单次更新场景)
如果你的需求只是把本次答题结果加到数据库里,不需要持续监听文档变化,那完全没必要用SnapshotListener,直接用get()方法一次性读取文档后再更新就好,从根源上避免循环:
public void addResultsToDB(final int right, final int wrong) { final FirebaseAuth fAuth = FirebaseAuth.getInstance(); final String userId = fAuth.getCurrentUser().getUid(); fStore = FirebaseFirestore.getInstance(); final DocumentReference documentReference = fStore.collection("usersData").document(userId); // 一次性读取用户文档 documentReference.get().addOnCompleteListener(task -> { if (task.isSuccessful() && fAuth.getCurrentUser() != null) { DocumentSnapshot value = task.getResult(); if (value.exists()) { // 读取当前数据(建议后续把字段存成Integer类型,不用每次转字符串) int totalN = Integer.parseInt(value.getString("TotalQuestions")); int rightN = Integer.parseInt(value.getString("RightAnswers")); int wrongN = Integer.parseInt(value.getString("WrongAnswers")); String theNameString = value.getString("uName"); String emailStringX = value.getString("Email"); // 组装更新后的数据 Map<String, Object> userDataX = new HashMap<>(); userDataX.put("uName", theNameString); userDataX.put("Email", emailStringX); userDataX.put("TotalQuestions", Integer.toString(totalN + 1)); userDataX.put("RightAnswers", String.valueOf(rightN + right)); userDataX.put("WrongAnswers", String.valueOf(wrongN + wrong)); // 更新文档,此时没有监听器,不会触发循环 documentReference.set(userDataX); } } }); }
方案2:保留监听器,但添加判断避免循环(适合需要持续监听的场景)
如果你确实需要一直监听文档变化,那就在更新前加个判断:只有当计算后的新值和当前文档的旧值不一样时,才执行更新操作,这样就不会触发无意义的循环了:
public void addResultsToDB(final int right, final int wrong) { final FirebaseAuth fAuth = FirebaseAuth.getInstance(); final String userId = fAuth.getCurrentUser().getUid(); fStore = FirebaseFirestore.getInstance(); final DocumentReference documentReference = fStore.collection("usersData").document(userId); documentReference.addSnapshotListener(this, (value, error) -> { if (fAuth.getCurrentUser() != null && value != null && value.exists()) { // 读取当前数据 int totalN = Integer.parseInt(value.getString("TotalQuestions")); int rightN = Integer.parseInt(value.getString("RightAnswers")); int wrongN = Integer.parseInt(value.getString("WrongAnswers")); // 计算更新后的值 int newTotal = totalN + 1; int newRight = rightN + right; int newWrong = wrongN + wrong; // 只有新值和旧值不同时才更新,避免循环触发 if (newTotal != totalN || newRight != rightN || newWrong != wrongN) { String theNameString = value.getString("uName"); String emailStringX = value.getString("Email"); Map<String, Object> userDataX = new HashMap<>(); userDataX.put("uName", theNameString); userDataX.put("Email", emailStringX); userDataX.put("TotalQuestions", Integer.toString(newTotal)); userDataX.put("RightAnswers", String.valueOf(newRight)); userDataX.put("WrongAnswers", String.valueOf(newWrong)); documentReference.set(userDataX); } } }); }
小提醒
另外建议你把Firestore里的TotalQuestions、RightAnswers、WrongAnswers字段改成整数类型,这样后续读取和计算的时候就不用每次都做字符串转整数的操作啦,既方便又能避免类型转换的潜在问题~
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

