首先仍是放上官方文档地址:developer.android.google.cn/topic/libra…html
若是你还不知道这个库是能够作什么事,能够看官方文档事例或者参考我以前写的这篇文章:android
目前该库还处于测试阶段,因此我就按上篇文章的内容进行部分简单的源码分析,从中你应该能够了解到该库的设计思路.bash
直接进入正题,先写简单的demo,首先建立咱们的model.网络
MainData:多线程
public class MainData extends AndroidViewModel {
/**
* 每次须要10个数据.
*/
private static final int NEED_NUMBER = 10;
/**
* 福利第一页.
*/
private static final int PAGE_FIRST = 1;
/**
* 分页.
*/
private int mPage = PAGE_FIRST;
/**
* 列表数据.
*/
private LiveData<PagedList<GankData>> mDataLiveData;
public MainData(@NonNull Application application) {
super(application);
}
public LiveData<PagedList<GankData>> getDataLiveData() {
initPageList();
return mDataLiveData;
}
/**
* 初始化pageList.
*/
private void initPageList() {
//获取dataSource,列表数据都从这里获取,
final DataSource<Integer, GankData> tiledDataSource = new TiledDataSource<GankData>() {
/**
* 须要的总个数,若是数量不定,就传COUNT_UNDEFINED.
*/
@Override
public int countItems() {
return DataSource.COUNT_UNDEFINED;
}
/**
* 返回须要加载的数据.
* 这里是在线程异步中执行的,因此咱们能够同步请求数据而且返回
* @param startPosition 如今第几个数据
* @param count 加载的数据数量
*/
@Override
public List<GankData> loadRange(int startPosition, int count) {
List<GankData> gankDataList = new ArrayList<>();
//这里咱们用retrofit获取数据,每次获取十条数据,数量不为空,则让mPage+1
try {
Response<BaseResponse<List<GankData>>> execute = RetrofitApi.getInstance().mRetrofit.create(AppService.class)
.getWelfare1(mPage, NEED_NUMBER).execute();
gankDataList.addAll(execute.body().getResults());
if (!gankDataList.isEmpty()) {
mPage++;
}
} catch (IOException e) {
e.printStackTrace();
}
return gankDataList;
}
};
//这里咱们建立LiveData<PagedList<GankData>>数据,
mDataLiveData = new LivePagedListProvider<Integer, GankData>() {
@Override
protected DataSource<Integer, GankData> createDataSource() {
return tiledDataSource;
}
}.create(0, new PagedList.Config.Builder()
.setPageSize(NEED_NUMBER) //每次加载的数据数量
//距离本页数据几个时候开始加载下一页数据(例如如今加载10个数据,设置prefetchDistance为2,则滑到第八个数据时候开始加载下一页数据).
.setPrefetchDistance(NEED_NUMBER)
//这里设置是否设置PagedList中的占位符,若是设置为true,咱们的数据数量必须固定,因为网络数据数量不固定,因此设置false.
.setEnablePlaceholders(false)
.build());
}
}复制代码
这里咱们须要建立LiveData<PagedList<GankData>>这个对象,对于LiveData以前已经讲过了,若是你还不知道这个原理,能够去看下我以前文章:juejin.im/post/5a03ed…app
首先咱们来看下PageList究竟是什么:首先PageList是个抽象类,而且提供Build模式填充数据,首先看下它的build类:异步
public static class Builder<Key, Value> {
private DataSource<Key, Value> mDataSource;
private Executor mMainThreadExecutor;
private Executor mBackgroundThreadExecutor;
private Config mConfig;
private Key mInitialKey;
/**
* Creates a {@link PagedList} with the given parameters.
* <p>
* This call will initial data and perform any counting needed to initialize the PagedList,
* therefore it should only be called on a worker thread.
* <p>
* While build() will always return a PagedList, it's important to note that the PagedList * initial load may fail to acquire data from the DataSource. This can happen for example if * the DataSource is invalidated during its initial load. If this happens, the PagedList * will be immediately {@link PagedList#isDetached() detached}, and you can retry * construction (including setting a new DataSource). * * @return The newly constructed PagedList */ @WorkerThread @NonNull public PagedList<Value> build() { if (mDataSource == null) { throw new IllegalArgumentException("DataSource required"); } if (mMainThreadExecutor == null) { throw new IllegalArgumentException("MainThreadExecutor required"); } if (mBackgroundThreadExecutor == null) { throw new IllegalArgumentException("BackgroundThreadExecutor required"); } if (mConfig == null) { throw new IllegalArgumentException("Config required"); } return PagedList.create( mDataSource, mMainThreadExecutor, mBackgroundThreadExecutor, mConfig, mInitialKey); }复制代码
这里咱们看到必须填的数据有至少有四个,第一个DataSource,不用说,本身手动要建立的,两个线程池,一个Config和一个key,咱们再来看下Config:async
public static class Config {
final int mPageSize;
final int mPrefetchDistance;
final boolean mEnablePlaceholders;
final int mInitialLoadSizeHint;
private Config(int pageSize, int prefetchDistance,
boolean enablePlaceholders, int initialLoadSizeHint) {
mPageSize = pageSize;
mPrefetchDistance = prefetchDistance;
mEnablePlaceholders = enablePlaceholders;
mInitialLoadSizeHint = initialLoadSizeHint;
}
/**
* Builder class for {@link Config}.
* <p>
* You must at minimum specify page size with {@link #setPageSize(int)}.
*/
public static class Builder {
private int mPageSize = -1;
private int mPrefetchDistance = -1;
private int mInitialLoadSizeHint = -1;
private boolean mEnablePlaceholders = true;
/**
* Creates a {@link Config} with the given parameters.
*
* @return A new Config.
*/
public Config build() {
if (mPageSize < 1) {
throw new IllegalArgumentException("Page size must be a positive number");
}
if (mPrefetchDistance < 0) {
mPrefetchDistance = mPageSize;
}
if (mInitialLoadSizeHint < 0) {
mInitialLoadSizeHint = mPageSize * 3;
}
if (!mEnablePlaceholders && mPrefetchDistance == 0) {
throw new IllegalArgumentException("Placeholders and prefetch are the only ways"
+ " to trigger loading of more data in the PagedList, so either"
+ " placeholders must be enabled, or prefetch distance must be > 0.");
}
return new Config(mPageSize, mPrefetchDistance,
mEnablePlaceholders, mInitialLoadSizeHint);
}
}复制代码
这里一样采用Build模式,看下参数:mPageSize表明每次加载数量,mPrefetchDistance表明距离最后多少item数量开始加载下一页,mInittialLoadSizeHint表明首次加载数量(可是须要配合KeyedDataSource,咱们如今用TiledDataSource,因此忽略这个属性),mEnablePlaceholders表明是否设置null占位符,须要加载固定数量时候能够设置为true,若是数量不固定则设为false.ide
由于后面涉及到DataSource,因此咱们接下来先分析TiledDataSource.
TiledDataSource的父类DataSource.
// Since we currently rely on implementation details of two implementations,
// prevent external subclassing, except through exposed subclasses
DataSource() {
}
/**
* 数量不定的标志.
*/
@SuppressWarnings("WeakerAccess")
public static int COUNT_UNDEFINED = -1;
/**
* 总数量(数量不定返回COUNT_UNDEFINED).
*/
@WorkerThread
public abstract int countItems();
/**
* Returns true if the data source guaranteed to produce a contiguous set of items,
* never producing gaps.
*/
abstract boolean isContiguous();
/**
* Invalidation callback for DataSource.
* <p>
* Used to signal when a DataSource a data source has become invalid, and that a new data source
* is needed to continue loading data.
*/
public interface InvalidatedCallback {
/**
* Called when the data backing the list has become invalid. This callback is typically used
* to signal that a new data source is needed.
* <p>
* This callback will be invoked on the thread that calls {@link #invalidate()}. It is valid
* for the data source to invalidate itself during its load methods, or for an outside
* source to invalidate it.
*/
@AnyThread
void onInvalidated();
}
/**
* 数据可用的标志.
*/
private AtomicBoolean mInvalid = new AtomicBoolean(false);
/**
* 回调的线程安全集合.
*/
private CopyOnWriteArrayList<InvalidatedCallback> mOnInvalidatedCallbacks =
new CopyOnWriteArrayList<>();
/**
* Add a callback to invoke when the DataSource is first invalidated.
* <p>
* Once invalidated, a data source will not become valid again.
* <p>
* A data source will only invoke its callbacks once - the first time {@link #invalidate()}
* is called, on that thread.
*
* @param onInvalidatedCallback The callback, will be invoked on thread that
* {@link #invalidate()} is called on.
*/
@AnyThread
@SuppressWarnings("WeakerAccess")
public void addInvalidatedCallback(InvalidatedCallback onInvalidatedCallback) {
mOnInvalidatedCallbacks.add(onInvalidatedCallback);
}
/**
* Remove a previously added invalidate callback.
*
* @param onInvalidatedCallback The previously added callback.
*/
@AnyThread
@SuppressWarnings("WeakerAccess")
public void removeInvalidatedCallback(InvalidatedCallback onInvalidatedCallback) {
mOnInvalidatedCallbacks.remove(onInvalidatedCallback);
}
/**
* 对外方法,执行回调(可是未发现任何地方调用了这个方法,估计是为了之后扩展用).
*/
@AnyThread
public void invalidate() {
if (mInvalid.compareAndSet(false, true)) {
for (InvalidatedCallback callback : mOnInvalidatedCallbacks) {
callback.onInvalidated();
}
}
}
/**
* Returns true if the data source is invalid, and can no longer be queried for data.
*
* @return True if the data source is invalid, and can no longer return data.
*/
@WorkerThread
public boolean isInvalid() {
return mInvalid.get();
}复制代码
这里须要先说下AtomicBoolean和CopyOrWriteArrayList,
AtomicBoolean:按照文档说明,大概意思就是以原子的方式更新bollean值,其中
CopyOrWriteArrayList是一个线程安全的list,它的 add和remove等一些方法都是加锁了.
再来看下TiledDataSource:
public abstract class TiledDataSource<Type> extends DataSource<Integer, Type> {
@WorkerThread
@Override
public abstract int countItems();
@Override
boolean isContiguous() {
return false;
}
@WorkerThread
public abstract List<Type> loadRange(int startPosition, int count);
final List<Type> loadRangeWrapper(int startPosition, int count) {
if (isInvalid()) {
return null;
}
List<Type> list = loadRange(startPosition, count);
if (isInvalid()) {
return null;
}
return list;
}
ContiguousDataSource<Integer, Type> getAsContiguous() {
return new TiledAsBoundedDataSource<>(this);
}
/**
* BoundedDataSource是最终继承ContiguousDataSource的类,这个负责包装TiledDataSource.
*/
static class TiledAsBoundedDataSource<Value> extends BoundedDataSource<Value> {
final TiledDataSource<Value> mTiledDataSource;
TiledAsBoundedDataSource(TiledDataSource<Value> tiledDataSource) {
mTiledDataSource = tiledDataSource;
}
@WorkerThread
@Nullable
@Override
public List<Value> loadRange(int startPosition, int loadCount) {
return mTiledDataSource.loadRange(startPosition, loadCount);
}
}
}复制代码
loadRange方法咱们用不到暂时(没发现调用这个方法的地方),咱们只须要实现countItems和loadRange方法,咱们例子中是无限制数量,因此传了-1,而loadRange方法是在异步线程中执行,因此这里能够用网络同步请求返回数据.
而后接下来看PagedList的create方法.
@NonNull
private static <K, T> PagedList<T> create(@NonNull DataSource<K, T> dataSource,
@NonNull Executor mainThreadExecutor,
@NonNull Executor backgroundThreadExecutor,
@NonNull Config config,
@Nullable K key) {
if (dataSource.isContiguous() || !config.mEnablePlaceholders) {
if (!dataSource.isContiguous()) {
//noinspection unchecked
dataSource = (DataSource<K, T>) ((TiledDataSource<T>) dataSource).getAsContiguous();
}
ContiguousDataSource<K, T> contigDataSource = (ContiguousDataSource<K, T>) dataSource;
return new ContiguousPagedList<>(contigDataSource,
mainThreadExecutor,
backgroundThreadExecutor,
config,
key);
} else {
return new TiledPagedList<>((TiledDataSource<T>) dataSource,
mainThreadExecutor,
backgroundThreadExecutor,
config,
(key != null) ? (Integer) key : 0);
}
}
复制代码
根据一系列条件,咱们获取的PagedList是ContiguousPagedList.
而后咱们能够来看LiveData<PagedList<GankData>>是怎么建立的了:
来看下LivePagedListProvider:
public abstract class LivePagedListProvider<Key, Value> {
/**
* Construct a new data source to be wrapped in a new PagedList, which will be returned
* through the LiveData.
*
* @return The data source.
*/
@WorkerThread
protected abstract DataSource<Key, Value> createDataSource();
/**
* Creates a LiveData of PagedLists, given the page size.
* <p>
* This LiveData can be passed to a {@link PagedListAdapter} to be displayed with a
* {@link android.support.v7.widget.RecyclerView}.
*
* @param initialLoadKey Initial key used to load initial data from the data source.
* @param pageSize Page size defining how many items are loaded from a data source at a time.
* Recommended to be multiple times the size of item displayed at once.
*
* @return The LiveData of PagedLists.
*/
@AnyThread
@NonNull
public LiveData<PagedList<Value>> create(@Nullable Key initialLoadKey, int pageSize) {
return create(initialLoadKey,
new PagedList.Config.Builder()
.setPageSize(pageSize)
.build());
}
/**
* Creates a LiveData of PagedLists, given the PagedList.Config.
* <p>
* This LiveData can be passed to a {@link PagedListAdapter} to be displayed with a
* {@link android.support.v7.widget.RecyclerView}.
*
* @param initialLoadKey Initial key to pass to the data source to initialize data with.
* @param config PagedList.Config to use with created PagedLists. This specifies how the
* lists will load data.
*
* @return The LiveData of PagedLists.
*/
@AnyThread
@NonNull
public LiveData<PagedList<Value>> create(@Nullable final Key initialLoadKey,
final PagedList.Config config) {
return new ComputableLiveData<PagedList<Value>>() {
@Nullable
private PagedList<Value> mList;
@Nullable
private DataSource<Key, Value> mDataSource;
private final DataSource.InvalidatedCallback mCallback =
new DataSource.InvalidatedCallback() {
@Override
public void onInvalidated() {
invalidate();
}
};
@Override
protected PagedList<Value> compute() {
@Nullable Key initializeKey = initialLoadKey;
if (mList != null) {
//noinspection unchecked
initializeKey = (Key) mList.getLastKey();
}
do {
if (mDataSource != null) {
mDataSource.removeInvalidatedCallback(mCallback);
}
mDataSource = createDataSource();
mDataSource.addInvalidatedCallback(mCallback);
mList = new PagedList.Builder<Key, Value>()
.setDataSource(mDataSource)
.setMainThreadExecutor(ArchTaskExecutor.getMainThreadExecutor())
.setBackgroundThreadExecutor(
ArchTaskExecutor.getIOThreadExecutor())
.setConfig(config)
.setInitialKey(initializeKey)
.build();
} while (mList.isDetached());
return mList;
}
}.getLiveData();
}
复制代码
代码很简单(忽略那些mCallback,现阶段彻底用不到),只要咱们重写传入DataSource,建立的主要类是ComputableLiveData干得,看一下:
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public abstract class ComputableLiveData<T> {
private final LiveData<T> mLiveData;
private AtomicBoolean mInvalid = new AtomicBoolean(true);
private AtomicBoolean mComputing = new AtomicBoolean(false);
/**
* Creates a computable live data which is computed when there are active observers.
* <p>
* It can also be invalidated via {@link #invalidate()} which will result in a call to
* {@link #compute()} if there are active observers (or when they start observing)
*/
@SuppressWarnings("WeakerAccess")
public ComputableLiveData() {
mLiveData = new LiveData<T>() {
@Override
protected void onActive() {
// TODO if we make this class public, we should accept an executor
ArchTaskExecutor.getInstance().executeOnDiskIO(mRefreshRunnable);
}
};
}
/**
* Returns the LiveData managed by this class.
*
* @return A LiveData that is controlled by ComputableLiveData.
*/
@SuppressWarnings("WeakerAccess")
@NonNull
public LiveData<T> getLiveData() {
return mLiveData;
}
/**
* mInvalid默认为true,这里能够一条线执行下去,PagedList就是上面那个compute里新建的对象,
* 而且执行了postValue方法,观察者那边能够就收到通知了.
*/
@VisibleForTesting
final Runnable mRefreshRunnable = new Runnable() {
@WorkerThread
@Override
public void run() {
boolean computed;
do {
computed = false;
// compute can happen only in 1 thread but no reason to lock others.
if (mComputing.compareAndSet(false, true)) {
// as long as it is invalid, keep computing.
try {
T value = null;
while (mInvalid.compareAndSet(true, false)) {
computed = true;
value = compute();
}
if (computed) {
mLiveData.postValue(value);
}
} finally {
// release compute lock
mComputing.set(false);
}
}
// check invalid after releasing compute lock to avoid the following scenario.
// Thread A runs compute()
// Thread A checks invalid, it is false
// Main thread sets invalid to true
// Thread B runs, fails to acquire compute lock and skips
// Thread A releases compute lock
// We've left invalid in set state. The check below recovers. } while (computed && mInvalid.get()); } }; // invalidation check always happens on the main thread @VisibleForTesting final Runnable mInvalidationRunnable = new Runnable() { @MainThread @Override public void run() { boolean isActive = mLiveData.hasActiveObservers(); if (mInvalid.compareAndSet(false, true)) { if (isActive) { // TODO if we make this class public, we should accept an executor. ArchTaskExecutor.getInstance().executeOnDiskIO(mRefreshRunnable); } } } }; } 复制代码
代码也是很简单的,先看构造方法,构造方法中直接新建了LiveData,而且在生命周期onStrart后执行mRefreshRunable线程,这个线程直接给LiveData赋值,而后activity注册的地方就能够收到PagedList的回调了.
咱们再来看看构造PagedList的默认线程池,追踪代码:
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class DefaultTaskExecutor extends TaskExecutor {
private final Object mLock = new Object();
private ExecutorService mDiskIO = Executors.newFixedThreadPool(2);
@Nullable
private volatile Handler mMainHandler;
@Override
public void executeOnDiskIO(Runnable runnable) {
mDiskIO.execute(runnable);
}
@Override
public void postToMainThread(Runnable runnable) {
if (mMainHandler == null) {
synchronized (mLock) {
if (mMainHandler == null) {
mMainHandler = new Handler(Looper.getMainLooper());
}
}
}
//noinspection ConstantConditions
mMainHandler.post(runnable);
}
@Override
public boolean isMainThread() {
return Looper.getMainLooper().getThread() == Thread.currentThread();
}
}
复制代码
就是这个Executors.newFixedThreadPool(2)线程池了,
接下来咱们看PagedList:
public abstract class PagedListAdapter<T, VH extends RecyclerView.ViewHolder>
extends RecyclerView.Adapter<VH> {
private final PagedListAdapterHelper<T> mHelper;
/**
* Creates a PagedListAdapter with default threading and
* {@link android.support.v7.util.ListUpdateCallback}.
*
* Convenience for {@link #PagedListAdapter(ListAdapterConfig)}, which uses default threading
* behavior.
*
* @param diffCallback The {@link DiffCallback} instance to compare items in the list.
*/
protected PagedListAdapter(@NonNull DiffCallback<T> diffCallback) {
mHelper = new PagedListAdapterHelper<>(this, diffCallback);
}
@SuppressWarnings("unused, WeakerAccess")
protected PagedListAdapter(@NonNull ListAdapterConfig<T> config) {
mHelper = new PagedListAdapterHelper<>(new ListAdapterHelper.AdapterCallback(this), config);
}
/**
* Set the new list to be displayed.
* <p>
* If a list is already being displayed, a diff will be computed on a background thread, which
* will dispatch Adapter.notifyItem events on the main thread.
*
* @param pagedList The new list to be displayed.
*/
public void setList(PagedList<T> pagedList) {
mHelper.setList(pagedList);
}
@Nullable
protected T getItem(int position) {
return mHelper.getItem(position);
}
@Override
public int getItemCount() {
return mHelper.getItemCount();
}
/**
* Returns the list currently being displayed by the Adapter.
* <p>
* This is not necessarily the most recent list passed to {@link #setList(PagedList)}, because a
* diff is computed asynchronously between the new list and the current list before updating the
* currentList value.
*
* @return The list currently being displayed.
*/
@Nullable
public PagedList<T> getCurrentList() {
return mHelper.getCurrentList();
}
}复制代码
能够看到,PagedListAdapter只是个壳,作事的是PagedListAdapterHelper.
咱们主要看PagedListAdapterHelper的这个几个方法:
public void setList(final PagedList<T> pagedList) {
if (pagedList != null) {
if (mList == null) {
mIsContiguous = pagedList.isContiguous();
} else {
if (pagedList.isContiguous() != mIsContiguous) {
throw new IllegalArgumentException("AdapterHelper cannot handle both contiguous"
+ " and non-contiguous lists.");
}
}
}
if (pagedList == mList) {
// nothing to do
return;
}
// incrementing generation means any currently-running diffs are discarded when they finish
final int runGeneration = ++mMaxScheduledGeneration;
if (pagedList == null) {
mUpdateCallback.onRemoved(0, mList.size());
mList.removeWeakCallback(mPagedListCallback);
mList = null;
return;
}
if (mList == null) {
// fast simple first insert
mUpdateCallback.onInserted(0, pagedList.size());
mList = pagedList;
pagedList.addWeakCallback(null, mPagedListCallback);
return;
}
if (!mList.isImmutable()) {
// first update scheduled on this list, so capture mPages as a snapshot, removing
// callbacks so we don't have resolve updates against a moving target mList.removeWeakCallback(mPagedListCallback); mList = (PagedList<T>) mList.snapshot(); } final PagedList<T> oldSnapshot = mList; final List<T> newSnapshot = pagedList.snapshot(); mUpdateScheduled = true; mConfig.getBackgroundThreadExecutor().execute(new Runnable() { @Override public void run() { final DiffUtil.DiffResult result; if (mIsContiguous) { result = ContiguousDiffHelper.computeDiff( (NullPaddedList<T>) oldSnapshot, (NullPaddedList<T>) newSnapshot, mConfig.getDiffCallback(), true); } else { result = SparseDiffHelper.computeDiff( (PageArrayList<T>) oldSnapshot, (PageArrayList<T>) newSnapshot, mConfig.getDiffCallback(), true); } mConfig.getMainThreadExecutor().execute(new Runnable() { @Override public void run() { if (mMaxScheduledGeneration == runGeneration) { mUpdateScheduled = false; latchPagedList(pagedList, newSnapshot, result); } } }); } }); } private void latchPagedList( PagedList<T> newList, List<T> diffSnapshot, DiffUtil.DiffResult diffResult) { if (mIsContiguous) { ContiguousDiffHelper.dispatchDiff(mUpdateCallback, (NullPaddedList<T>) mList, (ContiguousPagedList<T>) newList, diffResult); } else { SparseDiffHelper.dispatchDiff(mUpdateCallback, diffResult); } mList = newList; newList.addWeakCallback((PagedList<T>) diffSnapshot, mPagedListCallback); }复制代码
这个理解成给adapter设置集合,理论上设置一次,这里对于屡次设置集合作了刷新的比较处理。这里仍是用到了RecylerView的 DifffUtil工具.
固然这个库的主要功能是在未滑倒底部时候就进行数据加载,让用户无感知加载,这个处理方法就在getItem(int index)里:
@SuppressWarnings("WeakerAccess")
@Nullable
public T getItem(int index) {
if (mList == null) {
throw new IndexOutOfBoundsException("Item count is zero, getItem() call is invalid");
}
mList.loadAround(index);
return mList.get(index);
}复制代码
recyclerView滑动加载到第几个数据时候就会调用adapter的getItem方法,经过判断当前加载的index位置,还有用户设置的预加载位置,从而进行提早加载数据。这里调用的是PagedList的loadAround方法,如今咱们去找这个方法的实如今哪:
ContiguousPageList:
@Override
public void loadAround(int index) {
mLastLoad = index + mPositionOffset;
int prependItems = mConfig.mPrefetchDistance - (index - mLeadingNullCount);
int appendItems = index + mConfig.mPrefetchDistance - (mLeadingNullCount + mList.size());
mPrependItemsRequested = Math.max(prependItems, mPrependItemsRequested);
if (mPrependItemsRequested > 0) {
schedulePrepend();
}
mAppendItemsRequested = Math.max(appendItems, mAppendItemsRequested);
if (mAppendItemsRequested > 0) {
scheduleAppend();
}
}
@MainThread
private void schedulePrepend() {
if (mPrependWorkerRunning) {
return;
}
mPrependWorkerRunning = true;
final int position = mLeadingNullCount + mPositionOffset;
final T item = mList.get(0);
mBackgroundThreadExecutor.execute(new Runnable() {
@Override
public void run() {
if (mDetached.get()) {
return;
}
final List<T> data = mDataSource.loadBefore(position, item, mConfig.mPageSize);
if (data != null) {
mMainThreadExecutor.execute(new Runnable() {
@Override
public void run() {
if (mDetached.get()) {
return;
}
prependImpl(data);
}
});
} else {
detach();
}
}
});
}
@MainThread
private void prependImpl(List<T> before) {
final int count = before.size();
if (count == 0) {
// Nothing returned from source, stop loading in this direction
return;
}
Collections.reverse(before);
mList.addAll(0, before);
final int changedCount = Math.min(mLeadingNullCount, count);
final int addedCount = count - changedCount;
if (changedCount != 0) {
mLeadingNullCount -= changedCount;
}
mPositionOffset -= addedCount;
mNumberPrepended += count;
// only try to post more work after fully prepended (with offsets / null counts updated)
mPrependItemsRequested -= count;
mPrependWorkerRunning = false;
if (mPrependItemsRequested > 0) {
// not done prepending, keep going
schedulePrepend();
}
// finally dispatch callbacks, after prepend may have already been scheduled
for (WeakReference<Callback> weakRef : mCallbacks) {
Callback callback = weakRef.get();
if (callback != null) {
if (changedCount != 0) {
callback.onChanged(mLeadingNullCount, changedCount);
}
if (addedCount != 0) {
callback.onInserted(0, addedCount);
}
}
}
}
复制代码
因为咱们传入的是TiledDataSource,因此这些mDataSource的loadBefore和loadAfter等最终都调用了TiledDataSource的loadRange方法。
到这里为止,好像总体流程已经分析完了,总体流程就看这张图吧:
首先Recyclerview设置PagedListAdater,PagedListAdapter设置对应的PagedList,每次adapter getItme就让PagedList知道用户已经滑到第几个item,PagedList计算这些数量以及设置的各类条件,条件达成就通知DataSource,让其返回数据,数据返回成功,通知PagedListAdapter,让其使用进行高效刷新.
目前该库仍是测试阶段,里面还有方法回调都未被用到,本文只是简单分析下源码流程,不过也貌似打开了一些新的思路.
因为本人水平也不高,文中也有很多理解错误之处,也但愿能各位指教,一块儿学习,一块儿进步.