Firebase如何与共享数据同步?

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

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

Firebase在应用级别设置为离线function。 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); } } 

主要活动

 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); } } 

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

在此处输入图像描述

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

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

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

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

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

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

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

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

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

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

所以现在,我用

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

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

将@ 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保存的值。 请建议/纠正这个假设!