2017-09-18 72 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期間啓動和停止監聽器是傳統操作。如果應用程序暫時失去焦點(例如,它彈出一個對話框,或其他一些透明活動出現在頂部),這將避免更多的反饋。