Jetpack之Paging 源码分析

目录

一 LiveData>创建

二 数据初始化加载

三 数据的显示


一 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 对应的方法。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值