ValueEventListener 在 .runTransaction() 之后的一秒内返回空值快照
ValueEventListener returning a snapshot with null value for a brief second after .runTransaction()
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
if (snapshot.getValue() == null) {
Log.println(Log.ERROR, TAG, "onDataChange: WAS NULL!!!!");
} else {
//Do my thing.
}
}
从数据库中避免 null 没什么大不了的,但是如果执行并发事务,数据库首先返回 null 的事实是非常有问题的(?)。
而且我认为问题出在我的交易中,我尝试阅读此处的一些答案,但它们重定向到不再存在的 Firebase 文档(?),可用的文档是针对 Firestore API: https://firebase.google.com/docs/#section-transactions
我认为我的问题是 setValue() 的放置,因为它们都放在空检查中。
@NonNull
@Override
public Transaction.Result doTransaction(@NonNull MutableData mutableData) {
MutableData childA = mutableData.child(Keys.A);
MutableData childB = mutableData.child(Keys.B);
MutableData childC = mutableData.child(Keys.C);
assert outerKey != null;
MutableData bAReference = childB.child(Keys.Ba).child(dayKey).child(outerKey);
MutableData bBReference = childB.child(Keys.Bb).child(outerKey);
MutableData bCReference = childB.child(Keys.Bc).child(dayKey);
ObjectA anObject = childA.getValue(ObjectA.class);
if (anObject != null) { /**I think the issue is that I am setting values ONLY IF reading of anObject != null*/
// Another reason may be that the null check is performed too late when it should at an earlier stage.
anObject.setA(anObject.getA() + outerDelta); //Change some inner state values.
anObject.setB(newBaObject.getB()); //Change some inner state values.
childA.setValue(anObject); /**Set value inside != null check(??)*/
/**^^^^This (childA) is the one returning null for a second*/
ObjectBc bCObject = Builder.buildSomething(dayKey, bCReference.getValue(ObjectBc.class), outerNewValues); // Builds a new Object with something old and something new
if (!something) {
bAReference.setValue(newBaObject);
bBReference.setValue(newBbObject);
} else {
bAReference.setValue(null); //I dont know if these are returning null, but it doesn't
bBReference.setValue(null); // matter since the one above is returning null anyways
}
if (somethingElse || something) {
creator.accept(childC::child); /**Sequential side effect iterator function for child creation (Don't judge me please! it's supposed to be faster :))*/
}
bCReference.setValue(bCObject); /**Again: sets value inside != null check*/
}
return Transaction.success(mutableData);
}
另一个重要的信息是交易进入同一个分支,但是这个分支被分叉成3个主要的子分支,所以有点太分裂了。
Frank Van Puffellen 在之前的回答中说 ():
If the actual stored value is the same as the assumed current value,
the Firebase server writes the new value you specified.
(比较和交换)
所以我认为,因为我没有指定一个新值,并且在我的空检查器实际上有时间做一些有意义的事情之前,数据库确认了“假设”和“实际”方式之间的相等性(因为它放置得太晚了) ,新值被设置为空一秒钟,然后用户在设备上的缓存状态的帮助下设置正确的新值。
这个假设的问题是第二个(正确的)写入不应该执行,因为假设和实际应该总是匹配,因为实际现在是空的,空检查下面的代码永远不会被执行,因为它永远不会被执行not null 因为没有代码放在 null 检查之外。
最糟糕的是,像 anObject.setA(anObject.getA() + outerDelta);
这样依赖于先前状态的行无论如何都会写出正确的答案......这意味着 assumed 永远不会为空那时。
另一个原因可能是在交易成功完成后给出 null 的原因完全不同,这意味着交易执行良好...
当然,也可能是作为响应式组件(我有)编写的 ValueEventListener 有问题。
firebaser 在这里
最初将 null
作为事务处理程序中的当前值是预期的行为。事务处理程序使用客户端当前猜测的值调用,该值通常为空。如果初始猜测不正确,您的事务处理程序将再次调用,并提供对当前值的更新猜测。参见 a.o。
解决方案是 return 如果您得到 null
一个值,即使您知道该值永远不会实际写入数据库:
if (anObject != null) {
...
}
else {
mutableData.setValue("This is needed to get the correct flow"); //
}
如果您不希望 Firebase 立即为交易值触发本地事件,但仅在确认交易后才触发,您可以将布尔值第二个参数传递给 runTransaction
以表明这一点:
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
if (snapshot.getValue() == null) {
Log.println(Log.ERROR, TAG, "onDataChange: WAS NULL!!!!");
} else {
//Do my thing.
}
}
从数据库中避免 null 没什么大不了的,但是如果执行并发事务,数据库首先返回 null 的事实是非常有问题的(?)。
而且我认为问题出在我的交易中,我尝试阅读此处的一些答案,但它们重定向到不再存在的 Firebase 文档(?),可用的文档是针对 Firestore API: https://firebase.google.com/docs/#section-transactions
我认为我的问题是 setValue() 的放置,因为它们都放在空检查中。
@NonNull
@Override
public Transaction.Result doTransaction(@NonNull MutableData mutableData) {
MutableData childA = mutableData.child(Keys.A);
MutableData childB = mutableData.child(Keys.B);
MutableData childC = mutableData.child(Keys.C);
assert outerKey != null;
MutableData bAReference = childB.child(Keys.Ba).child(dayKey).child(outerKey);
MutableData bBReference = childB.child(Keys.Bb).child(outerKey);
MutableData bCReference = childB.child(Keys.Bc).child(dayKey);
ObjectA anObject = childA.getValue(ObjectA.class);
if (anObject != null) { /**I think the issue is that I am setting values ONLY IF reading of anObject != null*/
// Another reason may be that the null check is performed too late when it should at an earlier stage.
anObject.setA(anObject.getA() + outerDelta); //Change some inner state values.
anObject.setB(newBaObject.getB()); //Change some inner state values.
childA.setValue(anObject); /**Set value inside != null check(??)*/
/**^^^^This (childA) is the one returning null for a second*/
ObjectBc bCObject = Builder.buildSomething(dayKey, bCReference.getValue(ObjectBc.class), outerNewValues); // Builds a new Object with something old and something new
if (!something) {
bAReference.setValue(newBaObject);
bBReference.setValue(newBbObject);
} else {
bAReference.setValue(null); //I dont know if these are returning null, but it doesn't
bBReference.setValue(null); // matter since the one above is returning null anyways
}
if (somethingElse || something) {
creator.accept(childC::child); /**Sequential side effect iterator function for child creation (Don't judge me please! it's supposed to be faster :))*/
}
bCReference.setValue(bCObject); /**Again: sets value inside != null check*/
}
return Transaction.success(mutableData);
}
另一个重要的信息是交易进入同一个分支,但是这个分支被分叉成3个主要的子分支,所以有点太分裂了。
Frank Van Puffellen 在之前的回答中说 (
If the actual stored value is the same as the assumed current value, the Firebase server writes the new value you specified.
(比较和交换)
所以我认为,因为我没有指定一个新值,并且在我的空检查器实际上有时间做一些有意义的事情之前,数据库确认了“假设”和“实际”方式之间的相等性(因为它放置得太晚了) ,新值被设置为空一秒钟,然后用户在设备上的缓存状态的帮助下设置正确的新值。
这个假设的问题是第二个(正确的)写入不应该执行,因为假设和实际应该总是匹配,因为实际现在是空的,空检查下面的代码永远不会被执行,因为它永远不会被执行not null 因为没有代码放在 null 检查之外。
最糟糕的是,像 anObject.setA(anObject.getA() + outerDelta);
这样依赖于先前状态的行无论如何都会写出正确的答案......这意味着 assumed 永远不会为空那时。
另一个原因可能是在交易成功完成后给出 null 的原因完全不同,这意味着交易执行良好...
当然,也可能是作为响应式组件(我有)编写的 ValueEventListener 有问题。
firebaser 在这里
最初将 null
作为事务处理程序中的当前值是预期的行为。事务处理程序使用客户端当前猜测的值调用,该值通常为空。如果初始猜测不正确,您的事务处理程序将再次调用,并提供对当前值的更新猜测。参见 a.o。
解决方案是 return 如果您得到 null
一个值,即使您知道该值永远不会实际写入数据库:
if (anObject != null) {
...
}
else {
mutableData.setValue("This is needed to get the correct flow"); //
}
如果您不希望 Firebase 立即为交易值触发本地事件,但仅在确认交易后才触发,您可以将布尔值第二个参数传递给 runTransaction
以表明这一点: