2017-09-18 74 views
1

我正在开发一个显示项目列表的应用程序,并允许用户添加更多项目并检查项目的详细信息。如何在使用Firebase数据库恢复活动时正确恢复数据?

比方说,我把我的数据库引用在活动中,加入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); 
    } 
} 

但是,如果我注销并重新注册监听器,例如,由任何方向改变或离开和returting到活动中,一如预期,onResume()将被调用,并通过再次加入ChildEventListener,同数据被检索到,如果它不与已经包含在项目列表中的数据进行比较mItems,它将被复制。另外,如果我没有保存项目列表mItems,并且每次调用onResume()时检索数据,则列表会丢失其滚动位置并且也会闪烁,因为检索数据需要一些时间。

问题

我怎能不再次再次检索它们检索到的项目列表,但要继续收听,在已经获取的项目更改,并且增加了新的项目?有没有更好的解决方案比比较已经检索的项目与新项目,例如,通过他们的关键?

我试过寻找文档和其他问题,但我找不到与这种情况有关的任何信息。另外,我已经试着将参考mDatabaseReference保留在onSaveInstanceState()中,正如我对mItems项目的列表所做的那样,但它也没有奏效。

+0

@rewgoes如果你有一个DatabaseHelper类,并且你使用RecyclerAdapter作为一个View Holder,如果你可以将你的数据加载到一个ArrayList中,那么当你添加Update或Delete时你只需要去一个数据库,所以这个是我使用Sqlite的MVP设计 – Grendel

+0

@grendel,我没有使用DatabaseHelper。我的数据库完全基于Firebase实时数据库。 – rewgoes

+0

@MarianoCórdoba这些模式本身并不一定会帮助OP正在经历的问题。您以这种方式构建的基础架构仍然容易被拆除,并且每次配置更改都会重新创建基础架构,除非您仍然执行一些特殊的操作来保留数据模型层。 –

回答

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

OP正在使用一个子监听器来持续监听数据库中特定位置的更改。考虑到随着时间的推移,更新会逐渐消失,这种策略将不会很好地发挥作用,因为Loaders倾向于认为整个数据负载仅加载一次。 –

+0

我同意你的意见,但我所说的是关于方向改变问题。让孩子听者注册并正常使用它。但最初使用装载机读一次。当你回到应用程序onStart()时使用它们。 – Aman

+0

同样如您在答案中所写,我们可以使用加载程序读取一次并缓存数据并脱机使用它。但即使缓存,您也要第一次阅读(即使您不使用onStart()情况并查询脱机数据) – Aman

2

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

FirebaseDatabase.getInstance().setPersistenceEnabled(true); 

一定要read the documentation了解如何工作的。

还要记住,在onStart和onStop期间启动和停止监听器是传统操作。如果应用程序暂时失去焦点(例如,它弹出一个对话框,或其他一些透明活动出现在顶部),这将避免更多的反馈。