我正在创建一个SCUBA潜水日志的Android应用程序。将现有的“潜水日志”更新到我的Firebase数据库后,只会调用一次onChildChanged事件,只能调用一次setValue。从我的logcat开始,第一次onChildChanged在setValue调用后发生0.034秒。第二个onChildChanged在第一个onChildChanged(原始setValue调用后0.126秒)后0.092秒发生。
以下是相关代码和我的数据结构。 任何想法为什么我收到两个onChildChanged事件一次调用setValue将不胜感激。
// save method in my DiveLog class
public static String save(@NonNull String userUid, @NonNull DiveLog diveLog) {
if (diveLog.getDiveLogUid() == null
|| diveLog.getDiveLogUid().isEmpty()
|| diveLog.getDiveLogUid().equals(MySettings.NOT_AVAILABLE)) {
String newDiveLogUid = nodeUserDiveLogs(userUid).push().getKey();
diveLog.setDiveLogUid(newDiveLogUid);
Timber.i("Created diveLog number %d.", diveLog.getDiveNumber());
}
nodeUserDiveLogs(userUid).child(diveLog.getDiveLogUid()).setValue(diveLog);
Timber.i("Saved diveLog number %d.", diveLog.getDiveNumber());
return diveLog.getDiveLogUid();
}
// my DiveLogs DatabaseReference
public static DatabaseReference nodeUserDiveLogs(@NonNull String userUid) {
return dbReference.child(NODE_DIVE_LOGS).child(userUid);
}
// my .addChildEventListener
private void addUserDiveLogsChildEventListener() {
mUserDiveLogsChildEventListener = new UserDiveLogsChildEventListener();
DiveLog.nodeUserDiveLogs(mUserUid)
.addChildEventListener(mUserDiveLogsChildEventListener);
}
// my onChildChanged event
public void onChildChanged(DataSnapshot dataSnapshot, String previousDiveLogUid) {
// TODO: Figure out why onChildChanged is called twice for each DiveLog.save().
if (dataSnapshot.getValue() != null) {
DiveLog changedDiveLog = dataSnapshot.getValue(DiveLog.class);
Timber.i("DiveLog: onChildChanged(). Dive Log Number: %d", changedDiveLog.getDiveNumber());
}
}
// My database structure:
// diveLogs
// ---> UserUid <<< ChildEventListener Node
// ---> diveLogUid
// ---> diveLogField
// ---> diveLogField
// ---> diveLogField
// .
// .
// .
// ---> diveLogUid
// ---> diveLogField
// ---> diveLogField
// ---> diveLogField
// .
// .
// .
更新:2016年11月28日 对于可能感兴趣的任何人,这里有关于这个问题的更新。
我一直与firebase支持联系,他们已经确认此问题是一个错误。 [仅供参考,我在firebase支持方面的经验一直很好。他们一直在回答这个问题。]
2016年11月21日火力支持人员告诉我:
虽然已找到原因,但我们的工程师仍在寻找合适的解决方案。我已经跟进并将向您通报进展情况。
在此期间,您可以尝试手动检查潜水日志相等性以避免重复更改事件。您必须在Divelog上实现equals()和hashCode()。我用IDE来生成它。还可以选择使用EqualsBuilder实现深度相等。
但是,这种解决方法不会让您避免相同浮点值的第一个更改事件。也许,您可以从客户端的“潜水日志列表”中检索确切的潜水日志,然后比较而不是使用“临时”潜水日志来解决这个问题。
private DiveLog temp = null;
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String previousDiveLogUid) {
DiveLog changedDiveLog = dataSnapshot.getValue(DiveLog.class);
Timber.i("DiveLog: RAW onChildChanged(). Dive Log Number: %d",
changedDiveLog.getDiveNumber());
if (dataSnapshot.getValue() != null) {
if(temp != null &&
//check if the same dive log
temp.getDiveLogUid().equals(changedDiveLog.getDiveLogUid()) &&
//compare
temp.equals(changedDiveLog)){
//skip change event
return;
}
temp = changedDiveLog;
Timber.i("DiveLog: onChildChanged(). Dive Log Number: %d", changedDiveLog.getDiveNumber());
//do stuff
}
}
2016年11月22日火力支持支持随后提出了这个建议: 我们工程师建议的解决方法是确保在编写整数时使用多头。 E.g:
if (Math.floor(x) == x) {
ref.set((long)x);
}
请注意,这假设您的值介于LONG_MIN和LONG_MAX之间。否则,您可能需要一些额外的逻辑,因此您不需要将较大的数字转换为较长的数字。
当你在一个类中使用它时,现在,你可以在该字段的直接引用上执行更新并从类中删除该字段(很多工作)。
很抱歉,但这肯定是SDK中的一个错误,解决方案并不完全是直截了当的,因此我们无法分享任何明确的日期,此时此修复程序将会发布。
最后下面是重现问题的简化代码:
package divelog.divelog;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import com.google.firebase.database.ChildEventListener;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import java.util.Map;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
final DatabaseReference ref = FirebaseDatabase.getInstance().getReference("divelog");
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Changing", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
//No issue with decimals
//Foo foo = new Foo(0.1f);
//Problem with saving floats that don't have decimals
Foo foo = new Foo(1f);
ref.child("child1").setValue(foo);
}
});
ref.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
Log.i("TEST", "changed: " + dataSnapshot.getValue());
Log.i("TEST", "changed: " + ((Map)dataSnapshot.getValue()).get("val").getClass());
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
Foo foo = new Foo(1f);
ref.child("child1").setValue(foo);
}
}
package divelog.divelog;
public class Foo {
private float val;
public Foo(float val) {
this.val = val;
}
public Foo() {
}
public float getVal() {
return val;
}
public void setVal(float val) {
this.val = val;
}
@Override
public String toString() {
return "Foo{" +
"val=" + val +
'}';
}
}