最近开了一个新项目,使用的是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属性设置的足够大: