【问题标题】:How to properly restore data when resuming an activity using Firebase Database?使用 Firebase 数据库恢复活动时如何正确恢复数据?
【发布时间】:2017-09-18 02:09:39
【问题描述】:

我正在开发一个显示项目列表并允许用户添加更多项目并检查项目详细信息的应用程序。

假设我在活动中保留我的数据库引用,添加ChildEventListener 侦听器onResume() 并在onPause() 上删除它们:

public void onResume() {
   super.onResume();
   mItemChildEventListener = mDatabaseReference.child('items').addChildEventListener(new ChildEventListener() {
       public void onChildAdded(DataSnapshot dataSnapshot, String s) {
           mAdapter.addItem(dataSnapshot.getValue(Item.class));
       }
       ...
   };
}

public void onPause() {
    super.onPause();
    if (mItemChildEventListener != null) {
        mDatabaseReference.removeEventListener(mItemChildEventListener);
    }
}

在这些ChildEventListener 中,我将逐项添加到项目列表中,并在适配器中调用notifyItemInserted(itemArray.size() - 1)

public void addItem(Item item) {
    if (mItems == null) {
        mItems = new ArrayList<>();
    }
    mItems.add(item);
    notifyItemInserted(mItems.size() - 1);
}

另外,为了让应用程序顺利运行,我将项目列表保存在 Reteiner Fragment 中,将这些值保存在 onSaveInstanceState() 并在 onCreate() 上恢复它们​​。

protected void onCreate(@Nullable Bundle savedInstanceState) {
    if (savedInstanceState != null) {
        mItems = getState(STATE_ITEMS);
    } else {
        mItems = new ArrayList<>();
    }

    mAdapter = new ItemAdapter(mItems);
}

public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    saveState(STATE_ITEMS, mItems);
}

public <T> T getState(String key) {
    //noinspection unchecked
    return (T) mRetainedFragment.map.get(key);
}

public void saveState(String key, Object value) {
    mRetainedFragment.map.put(key, value);
}

public static class RetainedFragment extends Fragment {
    HashMap<String, Object> map = new HashMap<>();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // retain this fragment
        setRetainInstance(true);
    }
}

但是,如果我取消注册并再次注册侦听器,例如,通过任何方向更改或离开和返回活动,如预期的那样,将调用 onResume(),并再次添加 ChildEventListener,同样数据被检索,如果它没有与项目列表中已经包含的数据进行比较,mItems,它将被复制。

另外,如果我不保留项目列表mItems,并在每次调用onResume() 时检索数据,列表将失去滚动位置并且还会闪烁,因为检索数据需要一些时间.

问题

如何保持检索到的项目列表而不再次检索它们,但继续监听已检索项目的变化和添加的新项目?是否有比将已检索到的项目与新项目进行比较(例如,通过它们的键)更好的解决方案?

我已尝试查看文档和其他问题,但找不到与这种情况相关的任何信息。另外,我试图在onSaveInstanceState() 中保留对mDatabaseReference 的引用,就像我对mItems 的项目列表所做的那样,但它也没有工作。

【问题讨论】:

  • @rewgoes 您是否有一个 DatabaseHelper 类,并且您是否使用 RecyclerAdapter 作为 View Holder,如果您可以将数据加载到 ArrayList 中,那么您只需在添加时访问数据库更新或删除,这是我与 Sqlite 一起使用的 MVP 设计
  • @grendel,我没有使用 DatabaseHelper。我的数据库完全基于 firebase 实时数据库。
  • @MarianoCórdoba 仅凭这些模式不一定能解决 OP 遇到的问题。您以这种方式构建的基础架构仍然容易因每次配置更改而被拆除和重新创建,除非您仍然做一些特殊的事情来保留您的数据模型层。

标签: android firebase firebase-realtime-database


【解决方案1】:

如果您在 Firebase 实时数据库 SDK 中使用enable persistence,这将有助于防止不必要地提取已收到的数据。 SDK 将在本地缓存获取数据,并在可用时优先使用该数据:

FirebaseDatabase.getInstance().setPersistenceEnabled(true);

请务必read the documentation 了解其工作原理。

还要记住,在 onStart 和 onStop 期间启动和停止侦听器是传统的做法。如果应用暂时失去焦点(例如,它会弹出一个对话框,或顶部出现一些其他透明活动),这将避免更多的重新获取。

【讨论】:

    【解决方案2】:
    1. 只需在加载器中调用 Firebase 并将数据保存在静态变量或列表或任何适合您的数据结构中。
    2. 在 onCreate 中初始化加载器。
    3. 在 Loader 中,重写 onStartLoading() 方法并在 onStart() 中调用此方法。
    4. 在 onStartLoading() 内部只检查静态变量是否为空。如果为 null,则 startLoading。否则不加载,将之前的数据设置为数据源。
    5. 使用加载器的好处是,在方向改变的情况下,它不会像 AsyncTask 那样进行网络调用。

    【讨论】:

    • OP 正在使用子侦听器来持续侦听数据库中特定位置的更改。考虑到更新会随着时间的推移而涓涓细流,这种策略不会很好地发挥作用,因为加载器倾向于假设整个数据负载只加载一次。
    • 我同意你的说法,但我所说的是关于方向变化的问题。保持子监听器注册并正常使用。但是最初使用加载器阅读一次。并在您返回应用程序 onStart() 时使用它们。
    • 正如您在答案中所写,我们可以使用加载器读取一次并缓存数据并离线使用。但是即使要缓存,你也要第一次读取(即使你不使用 onStart() 案例和查询离线数据)
    • Firebase 子侦听器不断地收听,并且在您告诉他们停止之前他们不会停止。随着时间的推移,他们会收到一组可能无限的事件。这不适用于加载器,加载器一次假设一个有限的数据集。
    猜你喜欢
    • 2019-07-31
    • 1970-01-01
    • 2021-04-17
    • 1970-01-01
    • 1970-01-01
    • 2020-07-07
    • 2015-10-04
    • 2010-11-07
    • 1970-01-01
    相关资源
    最近更新 更多