Android Firebase如何处理实时服务器到本地数据库的连接

时间:2019-01-13 13:00:21

标签: java android firebase firebase-realtime-database

对于与此主题和ChildEventListener有关的类似问题,没有相关的答案,所以这是我的。

我有一个本地SQLite数据库,其中保存所有数据,我还具有Firebase实时数据库,该数据库将通过新条目或所有用户的实时更改进行更新。我目前正在使用ChildEventListener来完成此操作,如下所示:

DatabaseReference rootRef = FirebaseDatabase.getInstance().getDatabase().getReference();
    DatabaseReference childRef = rootRef.child("my_root");

    ChildEventListener eventListener = new ChildEventListener()
    {
        ....
    };
    childRef.addChildEventListener(eventListener);

关于功能,使用此代码,我可以对子项进行实时更改,获取新条目,删除子项以及我需要的一切,但是有一个问题。当带有侦听器的特定活动加载完毕时,onChildAdded侦听器会为此根上的每个孩子调用大量时间,如文档所述:

  

为每个现有子级触发一次child_added,然后每次将新子级添加到指定路径时再次触发

所以我虽然专注于我真正需要的项目,但是我已经做到了:

rootRef.orderByKey().startAt("-WhatTF123456789")...

但是后来我失去了CRUD功能,因为它正在侦听新条目而不是全部。

所以我想出了一个解决方案。使节点保持对FireBase数据库所做的所有更改,并使节点保持所有已读取并对本地数据库进行了更改的用户,以了解谁需要更新,然后对这个特定节点使用addChildEventListener 。但这似乎是多余的。

如何处理这种情况?

3 个答案:

答案 0 :(得分:5)

  

onChildAdded侦听器为此根上的每个孩子被调用了很多次。

正如您已经提到的和文档所述,这是预期的行为。通常,不建议在包含大量数据的节点(根节点)上附加ChildEventListener。请注意这种做法,因为在下载大量数据时,您会得到类似OutOfMemoryError的错误信息。之所以发生这种情况,是因为您隐式下载了您正在侦听的整个节点以及其下的所有数据。该数据可能以简单属性或复杂对象的形式出现。因此,可以认为这是对资源和带宽的浪费。在这种情况下,最好的方法是尽可能地扁平化数据库。如果您是NoSQL数据库的新手,那么此做法称为 denormalization ,这是Firebase的常见做法。为了更好的理解,我建议您看一下:

还请注意,在复制数据时,请记住一件事。用与添加数据相同的方式,您需要对其进行维护。换句话说,如果您想更新/删除项目,则需要在它存在的每个位置进行。

我还建议您从以下帖子中查看答案的最后一部分:

它用于Cloud Firestore,但相同的规则适用于Firebase实时数据库。

  

但是后来我失去了CRUD功能,因为它正在侦听新条目而不是全部。

Firebase中的所有内容都与侦听器有关。除非正在侦听它们,否则无法获取节点内对象的实时更新。因此,您不能限制结果,也不能期望从不监听的对象获取更新。如果需要获取节点内所有对象的更新,则需要侦听所有对象。由于这种方法根本不实用,因此您可以如上所述使用 denormalization 或通过使用查询来限制结果,这些查询可以帮助您限制从数据库中获取的数据量。关于您的解决方案,第二种方法更为可取,但您也可以考虑另一种方法,该方法是根据timestamp属性或根据其他任何属性将数据装入较小的块您需要。

编辑:根据您的评论:

  

能否请您对每种解决方案(1.denormalization,2.my解决方案)进行测试,以检查带宽和资源的使用情况,哪一种才是真正的首选?

所有数据均经过建模以允许应用程序需要的用例。不幸的是,我无法进行测试,因为它实际上取决于应用程序的用例及其所包含的数据量。这意味着对一个应用程序有效的功能可能对另一应用程序而言是不够的。因此测试可能并非对每个人都是正确的。反规范化过程或您的解决方案完全取决于您打算如何查询数据库。在上面的列表中,我添加了一个新资源,这是我对denormalization tehnique in NoSQL databases的回答。希望它也能帮助访客。

答案 1 :(得分:1)

我将使用一个名称,例如str = malloc(sizeof(char*)*n); for (int i = 0; i < n; i++) { str[i] = malloc(sizeof(char)*n); } 的根节点。 所有客户都在这里订阅了更改。 MaintenanceUpdate = MaintenanceUpdate时,所有客户端都会取消订阅对主“数据库”的更改。然后(当true时)被重新订阅。 此时,您正在更新数据库。

答案 2 :(得分:1)

我对FirebaseRoom有类似的要求,而我已经这样解决了:

public class BaseModel extends BaseObservable implements IDataModel {

    /** Sqlite default PK */
    private int itemId = 0;

    /** Firebase uniqueId */
    @ColumnInfo(name = SqliteBaseHelper.KEY_FIREBASE_UNIQUE_ID)
    protected String uniqueId = null;

    /** Firebase lastSync */
    @ColumnInfo(name = SqliteBaseHelper.KEY_FIREBASE_LAST_SYNC)
    protected long lastSync = 0;

    ...
}

这意味着,当本地记录的KEY_FIREBASE_UNIQUE_IDnullKEY_FIREBASE_LAST_SYNC0时,必须将其插入Firebase中-否则,它将在运行同步AsyncTask时检查是否需要更新本地或远程记录。这是因为主要问题是,在远程插入时,ChildEventListener将尝试将重复项同步到同一客户端-除非在本地和远程均具有用于同步状态的指示器。本地主键可能会在客户端之间有所不同(取决于它们脱机多长时间以及在脱机状态下本地插入了多少条记录),而综合KEY_FIREBASE_UNIQUE_ID用于标识它们;这是“成功的关键”。