目录
一 LiveData<PagingList<T>>创建
使用LivePagedListBuilder配置Factory和Config,然后调用build创建实例,在build方法中直接调用了create()方法创建LiveDatacreate()
public LiveData<PagedList<Value>> build() {
return create(mInitialLoadKey, mConfig, mBoundaryCallback, mDataSourceFactory,
ArchTaskExecutor.getMainThreadExecutor(), mFetchExecutor);
}
private static <Key, Value> LiveData<PagedList<Value>> create(
@Nullable final Key initialLoadKey,
@NonNull final PagedList.Config config,
@Nullable final PagedList.BoundaryCallback boundaryCallback,
@NonNull final DataSource.Factory<Key, Value> dataSourceFactory,
@NonNull final Executor notifyExecutor,
@NonNull final Executor fetchExecutor) {
return new ComputableLiveData<PagedList<Value>>(fetchExecutor) {
@Nullable
private PagedList<Value> mList;
@Nullable
private DataSource<Key, Value> mDataSource;
private final DataSource.InvalidatedCallback mCallback =
new DataSource.InvalidatedCallback() {
@Override
public void onInvalidated() {
invalidate();
}
};
@SuppressWarnings("unchecked") // for casting getLastKey to Key
@Override
protected PagedList<Value> compute() {
@Nullable Key initializeKey = initialLoadKey;
if (mList != null) {
initializeKey = (Key) mList.getLastKey();
}
do {
if (mDataSource != null) {
mDataSource.removeInvalidatedCallback(mCallback);
}
// 1 从Builder中传入的Factory中创建DataSource
mDataSource = dataSourceFactory.create();
mDataSource.addInvalidatedCallback(mCallback);
// 2 创建PagedList
mList = new PagedList.Builder<>(mDataSource, config)
.setNotifyExecutor(notifyExecutor)
.setFetchExecutor(fetchExecutor)
.setBoundaryCallback(boundaryCallback)
.setInitialKey(initializeKey)
.build();
} while (mList.isDetached());
return mList;
}
}.getLiveData();
}
}
在Create 中返回ComputableLiveData的实例 ,现在我们来看看ComputableLiveData实例重写的compute中 做了哪些操作
1 创建DataSource 创建并返回PagedList实例 PagedList.build() & PagedList.create()
DataSource 是一个抽象的泛型类,接收两个泛型参数<Key,Value>,其中 Key 表示从数据源加载数据项的唯一标识,Value 与标识 Key 对应的数据项。DataSource 中定义了一个抽象的静态内部类 Factory<Key, Value>,是创建 DataSource 的工厂类。
DataSource 有两个直接的子类,分别是 PositionalDataSource 和 ContiguousDataSource。ContiguousDataSource 是一个非 public 的类,我们通常使用它的两个子类,PageKeyedDataSource 和 ItemKeyedDataSource 。
PositionalDataSource 和 ContiguousDataSource 这两个类最大的区别是对抽象方法 isContiguous() 的实现方式不同,PositionalDataSource 中的 isContiguous() 方法返回 false,ContiguousDataSource 中的 isContiguous() 方法返回 true。所以我们在代码中,能够使用的 DataSource 有三种,分别是 PositionalDataSource 和 ContiguousDataSource 的两个子类 PageKeyedDataSource 和 ItemKeyedDataSource 。
我们来看一下 PositionalDataSource、PageKeyedDataSource 和 ItemKeyedDataSource 分别适用哪些场景:
使用 PositionalDataSource,需要我们的实现类实现 loadInitial() 和 loadRange() 方法,适用于数据项总数固定,要通过特定的位置加载数据。比如从某个位置开始的 100 条数据;
使用 PageKeyedDataSource,需要实现 loadInitial()、loadBefore() 和 loadAfter() 方法,适用于以页信息加载数据的场景。比如在网络加载数据的时候,需要通过 setNextKey() 和 setPreviousKey() 方法设置下一页和上一页的标识 Key。
使用 ItemKeyedDataSource 除了需要实现 loadInitial()、loadBefore() 和 loadAfter() 方法以外,还要实现getKey() 方法,适用于所加载的数据依赖其他现有数据信息的场景。比如要加载的下一页的数据,依赖于当前页的数据。
public abstract class DataSource<Key, Value> {
/**
* Returns true if the data source guaranteed to produce a contiguous set of items,
* never producing gaps.
*/
abstract boolean isContiguous();
}
abstract class ContiguousDataSource<Key, Value> extends DataSource<Key, Value> {
@Override
boolean isContiguous() {
return true;
}
}
public abstract class PositionalDataSource<T> extends DataSource<Integer, T> {
@Override
boolean isContiguous() {
return false;
}
}
2 创建 PagedList ,PagedList 通过PagedList.build()中调用了PagedList.create()
static <K, T> PagedList<T> create(@NonNull DataSource<K, T> dataSource,
@NonNull Executor notifyExecutor,
@NonNull Executor fetchExecutor,
@Nullable BoundaryCallback<T> boundaryCallback,
@NonNull Config config,
@Nullable K key) {
if (dataSource.isContiguous() || !config.enablePlaceholders) {
int lastLoad = ContiguousPagedList.LAST_LOAD_UNSPECIFIED;
if (!dataSource.isContiguous()) {
//noinspection unchecked
dataSource = (DataSource<K, T>) ((PositionalDataSource<T>) dataSource)
.wrapAsContiguousWithoutPlaceholders();
if (key != null) {
lastLoad = (Integer) key;
}
}
ContiguousDataSource<K, T> contigDataSource = (ContiguousDataSource<K, T>) dataSource;
return new ContiguousPagedList<>(contigDataSource,
notifyExecutor,
fetchExecutor,
boundaryCallback,
config,
key,
lastLoad);
} else {
return new TiledPagedList<>((PositionalDataSource<T>) dataSource,
notifyExecutor,
fetchExecutor,
boundaryCallback,
config,
(key != null) ? (Integer) key : 0);
}
}
条件(dataSource.isContiguous() || !config.enablePlaceholders)分别创建ContiguousPagedList和TiledPagedList ,
代码中看出根据 我们创建的 CustomItemDataSource 继承ItemKeyedDataSource ItemKeyedDataSource 继承ContiguousDataSource
在ContiguousDataSource 中
abstract class ContiguousDataSource<Key, Value> extends DataSource<Key, Value> {
@Override
boolean isContiguous() {
return true;
}
dataSource.isContiguous() 为true 所以本例中创建的PagedList 是ContiguousPagedList
我们回头来看
ComputableLiveData
public ComputableLiveData(@NonNull Executor executor) {
mExecutor = executor;
mLiveData = new LiveData<T>() {
@Override
protected void onActive() {
mExecutor.execute(mRefreshRunnable);
}
};
}
@VisibleForTesting
final Runnable mRefreshRunnable = new Runnable() {
@WorkerThread
@Override
public void run() {
boolean computed;
do {
computed = false;
if (mComputing.compareAndSet(false, true)) {
try {
T value = null;
while (mInvalid.compareAndSet(true, false)) {
computed = true;
// 调用了compuet创建了PagedList
value = compute();
}
if (computed) {
//设置值
mLiveData.postValue(value);
}
} finally {
mComputing.set(false);
}
}
} while (computed && mInvalid.get());
}
};
在Runnable中调用了ComputableLiveData的compute()方法创建了PagedList,所以此处的Value就是PagedList,然后为mLiveData初始化赋值PagedList
二 数据初始化加载
从上面的执行过程中创建的PagedList实际为ContiguousPagedList
public class CustomItemDataSource extends ItemKeyedDataSource<Integer, Person> {
private DataRepository dataRepository;
CustomItemDataSource(DataRepository dataRepository) {
this.dataRepository = dataRepository;
}
// loadInitial 初始加载数据
@Override
public void loadInitial(@NonNull LoadInitialParams<Integer> params, @NonNull LoadInitialCallback<Person> callback) {
List<Person> dataList = dataRepository.initData(params.requestedLoadSize);
callback.onResult(dataList);
}
}
ContiguousPagedList(
@NonNull ContiguousDataSource<K, V> dataSource,
@NonNull Executor mainThreadExecutor,
@NonNull Executor backgroundThreadExecutor,
@Nullable BoundaryCallback<V> boundaryCallback,
@NonNull Config config,
final @Nullable K key,
int lastLoad) {
super(new PagedStorage<V>(), mainThreadExecutor, backgroundThreadExecutor,
boundaryCallback, config);
mDataSource = dataSource;
mLastLoad = lastLoad;
if (mDataSource.isInvalid()) {
detach();
} else {
mDataSource.dispatchLoadInitial(key,
mConfig.initialLoadSizeHint,
mConfig.pageSize,
mConfig.enablePlaceholders,
mMainThreadExecutor,
mReceiver);
}
mShouldTrim = mDataSource.supportsPageDropping()
&& mConfig.maxSize != Config.MAX_SIZE_UNBOUNDED;
}
public abstract class ItemKeyedDataSource<Key, Value> extends ContiguousDataSource<Key, Value> {
...
@Override
final void dispatchLoadInitial(@Nullable Key key, int initialLoadSize, int pageSize,
boolean enablePlaceholders, @NonNull Executor mainThreadExecutor,
@NonNull PageResult.Receiver<Value> receiver) {
LoadInitialCallbackImpl<Value> callback =
new LoadInitialCallbackImpl<>(this, enablePlaceholders, receiver);
loadInitial(new LoadInitialParams<>(key, initialLoadSize, enablePlaceholders), callback);
callback.mCallbackHelper.setPostExecutor(mainThreadExecutor);
}
...
}
从上面的代码可以知道 调用ItermKeyDataSource的dispatchLoadInitial方法 ,在ItermKeyDataSource的dispatchLoadInitial()方法中调用了抽象函数loadInitial()在我们的 CustomItemDataSource 在loadInitial()
初始化了数据
三 数据的显示
在自定义ItemDataSource的loadInitial()中加载数据后,调用了callback.onResult()方法,此处的callback是LoadInitialCallback的实现类LoadInitialCallbackImpl,在onResult()方法中又调用了LoadCallbackHelper.dispatchResultToReceiver()
void dispatchResultToReceiver(final @NonNull PageResult<T> result) {
Executor executor;
if (executor != null) {
executor.execute(new Runnable() {
@Override
public void run() {
mReceiver.onPageResult(mResultType, result);
}
});
} else {
mReceiver.onPageResult(mResultType, result);
}
}
在dispatchResultToReceiver()方法中,调用PageResult.Receiver.onPageResult()方法,这里的mReceiver是在调用 mDataSource.dispatchLoadInitial()时传入的最后一个参数,他的实现在ContiguousPagedList中
PageResult.Receiver<V> mReceiver = new PageResult.Receiver<V>() {
construction
@AnyThread
@Override
public void onPageResult(@PageResult.ResultType int resultType,
@NonNull PageResult<V> pageResult) {
if (pageResult.isInvalid()) {
detach();
return;
}
if (isDetached()) {
return;
}
List<V> page = pageResult.page;
if (resultType == PageResult.INIT) {
mStorage.init(pageResult.leadingNulls, page, pageResult.trailingNulls,
pageResult.positionOffset, ContiguousPagedList.this);
if (mLastLoad == LAST_LOAD_UNSPECIFIED) {
}
} else {
boolean trimFromFront = mLastLoad > mStorage.getMiddleOfLoadedRange();
boolean skipNewPage = mShouldTrim
&& mStorage.shouldPreTrimNewPage(
mConfig.maxSize, mRequiredRemainder, page.size());
if (resultType == PageResult.APPEND) {
if (skipNewPage && !trimFromFront) {
// don't append this data, drop it
mAppendItemsRequested = 0;
mAppendWorkerState = READY_TO_FETCH;
} else {
mStorage.appendPage(page, ContiguousPagedList.this);
}
} else if (resultType == PageResult.PREPEND) {
if (skipNewPage && trimFromFront) {
// don't append this data, drop it
mPrependItemsRequested = 0;
mPrependWorkerState = READY_TO_FETCH;
} else {
mStorage.prependPage(page, ContiguousPagedList.this);
}
} else {
throw new IllegalArgumentException("unexpected resultType " + resultType);
}
......
}
};
我们看 resultType 如何设置值的 在ItemKeyedDataSource类中LoadInitialCallbackImp 中lonResult dispatchLoadAfter 和dispatchLoadBefore 中
static class LoadInitialCallbackImpl<Value> extends LoadInitialCallback<Value> {
final LoadCallbackHelper<Value> mCallbackHelper;
private final boolean mCountingEnabled;
LoadInitialCallbackImpl(@NonNull ItemKeyedDataSource dataSource, boolean countingEnabled,
@NonNull PageResult.Receiver<Value> receiver) {
//1
mCallbackHelper = new LoadCallbackHelper<>(dataSource, PageResult.INIT, null, receiver);
mCountingEnabled = countingEnabled;
}
@Override
public void onResult(@NonNull List<Value> data, int position, int totalCount) {
if (!mCallbackHelper.dispatchInvalidResultIfInvalid()) {
LoadCallbackHelper.validateInitialLoadParams(data, position, totalCount);
int trailingUnloadedCount = totalCount - position - data.size();
if (mCountingEnabled) {
mCallbackHelper.dispatchResultToReceiver(new PageResult<>(
data, position, trailingUnloadedCount, 0));
} else {
mCallbackHelper.dispatchResultToReceiver(new PageResult<>(data, position));
}
}
}
@Override
public void onResult(@NonNull List<Value> data) {
if (!mCallbackHelper.dispatchInvalidResultIfInvalid()) {
mCallbackHelper.dispatchResultToReceiver(new PageResult<>(data, 0, 0, 0));
}
}
}
@Override
final void dispatchLoadAfter(int currentEndIndex, @NonNull Value currentEndItem,
int pageSize, @NonNull Executor mainThreadExecutor,
@NonNull PageResult.Receiver<Value> receiver) {
//2
loadAfter(new LoadParams<>(getKey(currentEndItem), pageSize),
new LoadCallbackImpl<>(this, PageResult.APPEND, mainThreadExecutor, receiver));
}
@Override
final void dispatchLoadBefore(int currentBeginIndex, @NonNull Value currentBeginItem,
int pageSize, @NonNull Executor mainThreadExecutor,
@NonNull PageResult.Receiver<Value> receiver) {
//3
loadBefore(new LoadParams<>(getKey(currentBeginItem), pageSize),
new LoadCallbackImpl<>(this, PageResult.PREPEND, mainThreadExecutor, receiver));
}
这就发现 上面注释 1 ,2, 3 处分别初始esultType 的值
在onPageResult()方法中根据resultType的类型执行操作,PageResult的三个数据类型分别对应者ItemKeyDataSource的三个方法:
loadInitial:对应初始化状态PageResult.INIT loadBefore:对应初始化状态PageResult.PREPEND loadAfter:对应初始化状态PageResult.APPEND
我们看PageResult Init方法
void init(int leadingNulls, @NonNull List<T> page, int trailingNulls, int positionOffset,
@NonNull Callback callback) {
init(leadingNulls, page, trailingNulls, positionOffset);
callback.onInitialized(size());
}
这个CallBack在哪里回调的呢 我们来看 PagedListAdapter
public void submitList(final PagedList<T> pagedList) {
if (mPagedList == null && mSnapshot == null) {
// fast simple first insert
mPagedList = pagedList;
pagedList.addWeakCallback(null, mPagedListCallback);
return;
}
}
private PagedList.Callback mPagedListCallback = new PagedList.Callback() {
@Override
public void onInserted(int position, int count) {
mUpdateCallback.onInserted(position, count);
}
@Override
public void onRemoved(int position, int count) {
mUpdateCallback.onRemoved(position, count);
}
@Override
public void onChanged(int position, int count) {
// NOTE: pass a null payload to convey null -> item
mUpdateCallback.onChanged(position, count, null);
}
};
在此处调用了PagedListAdapter 中submitList addWeakCallback()添加Callback实例mPagedListCallback, 而mPagedListCallback 中的mUpdateCallback 是 ListUpdateCallback 在Recyclerview包中AdapterListUpdateCallback 实现了 ListUpdateCallback
public final class AdapterListUpdateCallback implements ListUpdateCallback {
@NonNull
private final RecyclerView.Adapter mAdapter;
public AdapterListUpdateCallback(@NonNull RecyclerView.Adapter adapter) {
mAdapter = adapter;
}
/** {@inheritDoc} */
@Override
public void onInserted(int position, int count) {
mAdapter.notifyItemRangeInserted(position, count);
}
/** {@inheritDoc} */
@Override
public void onRemoved(int position, int count) {
mAdapter.notifyItemRangeRemoved(position, count);
}
/** {@inheritDoc} */
@Override
public void onMoved(int fromPosition, int toPosition) {
mAdapter.notifyItemMoved(fromPosition, toPosition);
}
/** {@inheritDoc} */
@Override
public void onChanged(int position, int count, Object payload) {
mAdapter.notifyItemRangeChanged(position, count, payload);
}
由上可知 mUPdateCallback方法 增加 删除 改变的方法 调用了 adapter 对应的方法。