【问题标题】:How does Firebase sync work, with shared data?Firebase 同步如何与共享数据一起工作?
【发布时间】:2016-01-20 12:25:03
【问题描述】:

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

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

Firebase 在应用级别设置为可离线使用。 userInstance POJO 在 log 时与 Firebase 同步,在 unlog 时停止同步。

我的用户配置文件中有一个特殊参数,我可以更改(作为管理员)。

每次我在 Firebase 控制台上更新它时,它都会在应用重新启动时替换为之前的值。

怎么会这样?

顺便说一句: 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);
    }
}

如果您只是更改应用程序中的值,则计数器会增加直到应用程序停止:这是正常的。但什么是链子,是时代从旧到新,再到旧的循环,没有停止。

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

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

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

【问题讨论】:

  • 不要描述您的代码,而是包含在问题中重现您的问题的最少代码。见stackoverflow.com/help/mcve
  • 恕我直言,系统问题,因为概念问题并不总是用代码更好地解释?顺便说一句,我创建了一个简单的应用程序,触发了一个问题,这似乎与我的问题有关。

标签: java android database synchronization firebase


【解决方案1】:

问题在于您将磁盘持久性与单值事件侦听器一起使用。当你这样做时:

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

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

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

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

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

【讨论】:

  • 好的,谢谢你的链接,它有助于理解。所以我理解你的建议来解决问题,我会尝试的。但是我不确定为什么这会在新旧值之间无限切换!看看我即将到来的理解场景。
  • 这种情况会这样吗? 1. GetValue : old (singleValueListener) 11. SaveValue : old 2. GetValue : new (因为它是keepsync(true)) 21. SaveValue : new 如果2在11完成之前注册,则循环!
  • 将 Single 改为 Normal 监听器并没有解决问题
  • 设置 Firebase.getDefaultConfig().setPersistenceEnabled(false);没有解决问题!
  • 这是否意味着无论缓存同步状态如何,我总是可以期望常规事件侦听器触发两次?第二次总是来自服务器的最新值?
【解决方案2】:

将@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 用于应用程序可以保存的价值以及第三方应用程序可以保存的价值。请建议/纠正这个假设!

【讨论】:

    【解决方案3】:

    经过一番消化,我目前的策略是使用 Firebase 进行数据持久化,不再使用我自己的对象。 (在我必须同步 UI、我的数据、firebase 缓存和服务器数据之前)

    所以现在,我使用

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-30
      • 2018-07-13
      相关资源
      最近更新 更多