最近開了一個新專案,使用的是Google 2018 IO大會 推薦的新的app架構,如下:
官方地址這裡主要講Paging + Room遇到的問題:
基礎的參考官方樣例: github.com/googlesampl…
PagedList的建立:
完全照搬官方的樣例,傳入自定義的BoundaryCallBack:PagedList的DataSource.Factory:
實現交由子類: Dao:OK,到這裡,基本上都是照搬的官方的樣例了,接下來就看執行結果了: 資料能正確獲取,並正確刷UI。但是,當資料超過30條(後面交代為什麼是30)時,具體效果如下: PagedList的長度為50,但是隻有30item是有資料的,其他都用null來佔位了,
這會造成一個問題,當我需要刪除一條資料時,刪除後重新整理UI,adapter的getItem()方法是會返回null,而我之所以用Paging + Room的形式,就是為了刪除,因為PagedList不支援刪除,233333。痛哭流涕啊。。。。。。。。
解題思路:
為什麼PagedList裡的其他資料是null,能不能把null去除
嘗試一、發現PagedList的Config中有enablePlaceholders(支援佔位)屬性,預設為true,支援null,能不能改為false,這樣PagedList中就不會有null
設定PagedList的配置enablePlaceholders為false後,PagedList的Observer接收到的資料並不完整,等於是PagedList將null資料過濾了。
只能往Room的原始碼挖了,為什麼會返回null:
1、將Room與PagedList聯絡起來的是新建PagedList傳入的Room產生的DataSource.Factory
3、Debug後發現,Cursor的長度與PagedList的Observer接收到的資料長度一致,所以由表入裡,看看這個方法的上游是哪,為什麼cursor中會有null的資料
LimitOffsetDataSource.loadRange()
從這個方法,我們能發現Cursor的由來,mDb.query(sqLiteQuery);,Sqlite語句sqLiteQuery的由來就有意思了, limit ? offset ?,查詢多少個,偏移多少(從第幾個開始),到這裡其實就有點眉目了而這兩個值其實來自與方法的引數: 4、查詢limit和offset的由來 LimitOffsetDataSource.loadInitial(): PositionalDataSource.computeInitialLoadPosition(): offset:
public static int computeInitialLoadPosition(@NonNull LoadInitialParams params,
int totalCount) {
int position = params.requestedStartPosition;
int initialLoadSize = params.requestedLoadSize;
int pageSize = params.pageSize;
int roundedPageStart = Math.round(position / pageSize) * pageSize; // 這裡肯定是大於0的
// maximum start pos is that which will encompass end of list
int maximumLoadPage = ((totalCount - initialLoadSize + pageSize - 1) / pageSize) * pageSize; // 所以必須保證maximumLoadPage小於等於0,所以必須保證 initialLoadSize必須足夠大
roundedPageStart = Math.min(maximumLoadPage, roundedPageStart);
// minimum start position is 0
roundedPageStart = Math.max(0, roundedPageStart); // 所以,roundedPageStart必須小於等於0
return roundedPageStart;
}
複製程式碼
如果想要將所有資料都載入出來,offset必須的保證為0,具體看上面程式碼註釋,所以關鍵在於params.requestedLoadSize
PositionalDataSource.computeInitialLoadSize(): limit:
public static int computeInitialLoadSize(@NonNull LoadInitialParams params,
int initialLoadPosition, int totalCount) {
return Math.min(totalCount - initialLoadPosition, params.requestedLoadSize); // 總的個數減去載入的起始位置 params.requestedLoadSize 兩數去最小值,為載入的總個數
}
複製程式碼
所以當params.requestedLoadSize足夠大時,資料庫中的所有資料都會被取出
Room加上limit邏輯,也是為了效率更高,但是因為有刪除的業務需求,導致異常,所以還是每次全部都取出,RecyclerView重新整理時,頁只會重新整理可見的Item,所以效能上還是OK的
5、查詢params.requestedLoadSize的由來
PositionalDataSource.dispatchLoadInitial(),來源於該方法的引數,還是得往上尋找:
解決方案
初始化PagedList時,將initialLoadSizeHint屬性設定的足夠大: