Firebase如何与共享数据同步?

时间:2015-10-21 13:19:08

标签: java android database synchronization firebase

我使用Firebase来处理Android应用的Auth主题。 我还在Firebase上保存了一个用户个人资料,其中包含用户ID以及用户可以在Android应用中更新的额外选项。

启动时,应用程序会检查身份验证和身份验证。它重新加载用户配置文件(在Firebase上),然后在应用程序上更新我的userInstance。

Firebase在应用级别设置为离线功能。 userInstance POJO在日志时与Firebase同步,并在unlog时停止同步。

我的用户个人资料中有一个特殊参数,我可以更改(作为管理员)。

每次我在Firebase控制台上更新它时,当应用重新开始时,它会被之前的值替换。

这怎么可能发生?

BTW: 1 /基于合并数据的机制,如果多个客户端具有不同的本地值?

这是更简单的代码,我尝试重现错误。 :

MyApplication.java

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Firebase.setAndroidContext(this);
        Firebase.getDefaultConfig().setLogLevel(Logger.Level.DEBUG);
        Firebase.getDefaultConfig().setPersistenceEnabled(true);


    }
}

MainActivity

public class MainActivity extends AppCompatActivity {

    Firebase ref;
    User user;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ref = new Firebase("https://millezim-test.firebaseIO.com").child("user");
        ref.keepSynced(true);

        Button br = (Button) findViewById(R.id.b_read);
        Button bs = (Button) findViewById(R.id.b_save);
        final TextView tv_r = (TextView) findViewById(R.id.tv_value_toread);
        final EditText tv_s = (EditText) findViewById(R.id.tv_value_tosave);

        user = new User();

        bs.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!tv_s.getText().toString().equalsIgnoreCase(""))
                    user.setAge(Integer.valueOf(tv_s.getText().toString()));
                ref.setValue(user);
            }
        });

        br.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ref.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        User u = dataSnapshot.getValue(User.class);
                        if (u != null)
                            tv_r.setText(String.valueOf(u.getAge()));
                        else
                            tv_r.setText("Bad Value");
                    }

                    @Override
                    public void onCancelled(FirebaseError firebaseError) {

                    }
                });
            }
        });

        ref.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                User u = dataSnapshot.getValue(User.class);
                u.setCounter(u.getCounter() + 1);
                user = u;
                saveUser();
            }

            @Override
            public void onCancelled(FirebaseError firebaseError) {

            }
        });
    }

    public void saveUser() {
        ref.setValue(user);
    }
}

如果您只是更改了应用中的值,那么计数器包含直到应用停止:这是正常的。但是什么是股票是从旧到新的循环而不是停止的旧循环。

enter image description here

我觉得我的应用程序中的行为,没有循环,因为我没有计数器,但我无法更改管理客户端中的参数,我总是收回存储在移动设备中的先前值。

我只是Auth,然后我用AuthData更新我的UserInstance +从Firebase获取用户(可能是缓存的数据),然后我在Firebase下保存更新的用户(因为我可能有新的AuthData,我通常得到来自Firebase的最新用户)

2 /在这个简单的例子中,我看到如果我在start时读取值,它会获取应用程序中缓存的数据。我如何强制拥有在线数据?

3 个答案:

答案 0 :(得分:10)

问题来自于您使用单值事件侦听器的磁盘持久性这一事实。当你这样做时:

ref.addListenerForSingleValueEvent(new ValueEventListener() {...

您要求该位置的单个值(您的情况下的用户)。如果Firebase在其本地缓存中具有该位置的值,则会立即触发该值。

解决此问题的最佳方法是不使用单值侦听器,而是使用常规事件侦听器。这样,您将获得两个事件:一个用于缓存版本,另一个用于从服务器返回的版本(如果不同)。

唯一的选择是不使用Firebase的磁盘持久性。如果没有这个,就不会有重新启动时读取数据的本地缓存。

Firebase邮件列表上有关于此组合的一些讨论。这是一个:https://groups.google.com/forum/#!msg/firebase-talk/ptTtEyBDKls/XbNKD_K8CQAJ

答案 1 :(得分:1)

消化后,我目前的策略是使用Firebase来保持数据持久性,而不再使用我自己的对象。 (在我必须同步UI,我的数据,firebase缓存和服务器数据之前)

现在,我使用

  • 使用磁盘缓存
  • 使用onValueEventListener
    • 保留更新数据(仅用于需要同步数据的组件读取)
    • 触发更新UI的事件(对于可以接受异步数据的组件)
  • 定义一些特定的setter,用于更新Firebase上的数据(不再是应用级别)

这意味着,当我更新数据时,它会转到服务器(或Firebase缓存层),直到它返回到UI。由于firebase处理这个缓存,如果速度足够快,这就是处理网络同步的Firebase。

答案 2 :(得分:0)

将@ frank-van-puffelen的(1)解决方案带入代码:

public class MainActivity extends AppCompatActivity {

    Firebase ref;
    User user;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ref = new Firebase("https://test.firebaseIO.com").child("user");
        ref.keepSynced(true);

        Button br = (Button) findViewById(R.id.b_read);
        Button bs = (Button) findViewById(R.id.b_save);
        final TextView tv_r = (TextView) findViewById(R.id.tv_value_toread);
        final EditText tv_s = (EditText) findViewById(R.id.tv_value_tosave);

        user = new User();

        bs.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!tv_s.getText().toString().equalsIgnoreCase(""))
                    user.setAge(Integer.valueOf(tv_s.getText().toString()));
                ref.setValue(user);
            }
        });

        br.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ref.addValueEventListener(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        User u = dataSnapshot.getValue(User.class);
                        if (u != null)
                            tv_r.setText(String.valueOf(u.getAge()));
                        else
                            tv_r.setText("Bad Value");
                    }

                    @Override
                    public void onCancelled(FirebaseError firebaseError) {

                    }
                });
            }
        });

        ref.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                User u = dataSnapshot.getValue(User.class);
                u.setCounter(u.getCounter() + 1);
                user = u;
                saveUser();
            }

            @Override
            public void onCancelled(FirebaseError firebaseError) {

            }
        });
    }

    public void saveUser() {
        ref.setValue(user);
    }
}

然而,这种改变没什么,甚至是最糟糕的。现在看来每个值设置,保持为鬼值(由客户端/服务器请求保持),并且可以在设置的每个值中看到值切换!

修改

以下代码已经解决了! 拥有一个普通的ValueListener,你在再次保存一个值之前停止了,并在保存完成时启用了! (好吧,我以为这可以在Firebase框架中完成)。

public class MainActivity extends AppCompatActivity {

    Firebase ref;
    User user;
    private ValueEventListener theListener;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ref = new Firebase("https://test.firebaseIO.com").child("user");
        ref.keepSynced(false);

        Button bs = (Button) findViewById(R.id.b_save);
        final EditText tv_s = (EditText) findViewById(R.id.tv_value_tosave);

        user = new User();

        bs.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!tv_s.getText().toString().equalsIgnoreCase(""))
                    user.setAge(Integer.valueOf(tv_s.getText().toString()));
                ref.setValue(user);
            }
        });


        theListener = new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                User u = dataSnapshot.getValue(User.class);
                u.setCounter(u.getCounter() + 1);
                user = u;
                updateUI(u);
                saveUser();
            }

            @Override
            public void onCancelled(FirebaseError firebaseError) {

            }
        };

        ref.addValueEventListener(theListener);
    }

    public void saveUser() {
        ref.removeEventListener(theListener);
        ref.setValue(user, new Firebase.CompletionListener() {
            @Override
            public void onComplete(FirebaseError firebaseError, Firebase firebase) {
                ref.addValueEventListener(theListener);
            }
        });
    }

    public void updateUI(User user) {

        TextView tv_r = (TextView) findViewById(R.id.tv_value_toread);
        if (user != null)
            tv_r.setText(String.valueOf(user.getAge()));
        else
            tv_r.setText("Bad Value");
    }
}

修改

但是,这不允许更改管理页面上的值。设置年龄值,然后保持回到移动设备上保存的年龄值。

所以我成像然后唯一的解决方案是在系统级解决。请勿使用VALUELISTENER以获得应用程序可以保存并且可以由第三方APP保存的值。请建议/纠正这个假设!