Firebase ChildEventListener调用onChildChanged两次调用setValue

时间:2016-10-20 22:15:10

标签: android firebase-realtime-database

我正在创建一个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 +
                '}';
    }
}

0 个答案:

没有答案