我有一个可以为某些物品提供报价的应用程序。想法是要约由我添加到我创建的另一个应用程序中,然后同时显示在用户设备上。
我只需要一个用户就可以接受要约,因此当第一个用户单击时,我确实要对要约ref调用removeValue()。已从数据库和其他用户recyclerview中正确删除了报价。
问题是,删除要约的同时发生2次单击,但是onChildRemoved()没有时间被调用,因此两个用户现在都拥有相同要约!
还有其他想法可以使此操作更精确,更省时吗?
根据svi.data的建议,更新,我在用户单击时尝试了这段代码,但是仍然出现相同的问题。
offerUnAnsweredRef.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
boolean stillThere = false;
for (DataSnapshot offerSnap : dataSnapshot.getChildren()) {
if (offerSnap.getKey().equals(requestedOffer.getCurrentNodeKey())) {
stillThere = true;
}
}
if (stillThere) {
Timber.d("We have it " + requestedOffer.getEmployeeKey());
Toast.makeText(getContext(), "Welcome Dear ", Toast.LENGTH_SHORT).show();
offerUnAnsweredRef.child(requestedOffer.getCurrentNodeKey()).removeValue();
} else {
Toast.makeText(getContext(), "Go Away Bear", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
更新2
实际上,该解决方案是在svi.data答案的基础上进行一些修改的,所以我想共享工作代码以帮助任何遇到类似情况的人
offerUnAnsweredRef.child(requestedOffer.getCurrentNodeKey()).runTransaction(new Transaction.Handler() {
@NonNull
@Override
public Transaction.Result doTransaction(@NonNull MutableData mutableData) {
RequestedOffer o = mutableData.getValue(RequestedOffer.class);
if (o == null) {
return Transaction.abort();
}
if (o.getEmployeeKey() == null) {
o.setEmployeeKey(employee.getUid());
mutableData.setValue(o);
return Transaction.success(mutableData);
} else {
return Transaction.success(mutableData);
}
}
@Override
public void onComplete(DatabaseError databaseError, boolean b,
DataSnapshot dataSnapshot) {
// check if the transaction completed successfully
// or if it failed
RequestedOffer o = dataSnapshot.getValue(RequestedOffer.class);
if (o.getEmployeeKey() == employee.getUid()) {
getActivity().runOnUiThread(() -> Toast.makeText(getActivity(), "Hello", Toast.LENGTH_SHORT).show());
DatabaseReference databaseReference = FirebaseFactory.getDatabase()
} else {
getActivity().runOnUiThread(() -> Toast.makeText(getActivity(), "NO", Toast.LENGTH_SHORT).show());
}
}
因为文档中所述
公共抽象Transaction.Result doTransaction(MutableData currentData)
可能会多次调用此方法,并在此位置使用当前数据。它负责检查这些数据并返回一个Transaction.Result,以指定该位置所需的新数据或应该中止该事务。
所以我添加了代码以检入onComplete以确保它仅调用一次。
答案 0 :(得分:2)
据我了解:
1)您有一个特定的应用程序(由您添加)。
2)您有另一个应用程序(按用户)读取优惠。
3)如果是这种情况,则两个应用程序都使用同一项目。
4)用户单击要约时,他/她将获得要约,然后您将从数据库中删除要约。
5)现在,当2个用户单击相同的商品时,没有时间将该商品从另一个用户的列表中删除,因此他们最终得到了相同的商品。
现在看来您不希望用户获得相同的报价,而问题确实是时间问题。
可能的解决方案:
1)每当用户单击要约时,就对数据库中的offers
节点运行ValueEventListener()并检查要约是否存在。
2)如果存在报价,请给他/她报价并将其删除。
3)现在,当2个用户单击同一报价时,我所讨论的ValueEventListener将为您提供一些时间,然后做出响应。
4)因此,用户不应最终获得相同的优惠。
希望它可以解决您的问题。
更新:
由于这是用户之间的竞争条件,因此该谈论交易了。 Firebase提供了一种直接读取并写入同一节点的并发更新的好方法(这就是您的情况)。
我希望您的数据库是这样的:
Offers
|
|------offer_id_1
|
|-----taken:false
|-----......
|-----......
|
|-------offer_id_2
|
|------taken:false
|------......
|------......
让我解释一下上面的结构,您从其他应用程序发布的每个报价在默认情况下都将具有一个名为taken
的标志,并且在默认情况下其值应为false
。
现在您在offer_id_1
和offer_id_2
上方看到的是为商品提供的推送ID或随机ID(当用户单击商品时,您必须获得此密钥的引用...我以为您知道该怎么做)。
在我们开始课程之前,您应该为您的帖子提供一个模型课程,我们将其称为Offer
,它只是一个课程:
public class Offer{
public boolean taken;
......
......
}
以下功能是您在有人点击要约(我们将使用交易)后将调用的功能:
public void RunTransactionFor(String offer_id){
//first refer to your offers
DatabaseReference offers_ref = FirebaseDatabase.getInstance().getReference().child("offers").child(offer_id);
//run a transaction (a transaction is fast it reads and writes directly)
offer_ref.runTransaction(new Transaction.Handler() {
@Override
public Transaction.Result doTransaction(MutableData mutableData) {
//this is a ref to the offer class
Offer offer = mutableData.getValue(Offer.class);
if (offer == null) {
return Transaction.success(mutableData);
}
if(offer.taken == false){
//take the offer
offer.taken = true;
//show a message
Toast.makeText(context, "you took the offer",...).show();
//now you can remove the offer
offers_ref.setValue(null);//or delete it your way
}else{
//too late the offer is taken
Toast.makeText(context, "too late the offer is gone",...).show();
//do nothing
}
// Set value and report transaction success
mutableData.setValue(offer);
return Transaction.success(mutableData);
}
@Override
public void onComplete(DatabaseError databaseError, boolean b,
DataSnapshot dataSnapshot) {
// Transaction completed
}
});
}
现在,当用户单击列表中的要约时,请存储要约的ID,并将其传递给上述功能
//after user clicks
String offer_id = .......
//run transaction
RunTransactionFor(offer_id);
注意:交易只能在线工作,而不能离线工作。