android分页框架Paging源码分析

如果你还没用过Paging的话,很正常,毕竟现在用的并不多,做为android的开发人员分页的做法我们习惯写一个自定义控件,下拉的时候刷新(请求接口的页数置为1),上拉加载更多(请求接口数据页数实现累加)。然而,一种新的框架出来,我们应该尽可能的去了解它的架构,吸取人家谷歌工程师的代码精华。

好了,现在咱们先来了解一下怎么样用用这个分页库:

第一步:引入分页库

implementation "android.arch.paging:runtime:2.1.0"

既然是分页那么肯定用RecyclerView,那么

第二步:为RecyclerView设置适配器,实现适配器PagedListAdapter这个类,也就是我们常用的适配器的子类。(用法和普通适配器的用法一样)。

第三步:PagedListAdapter实现DiffUtil.ItemCallback,这个类为判断数据集合是否一样提供了分析,没用过DiffUtil的小伙伴请自行谷歌一下它的用法。

第四步:用LivePagedListBuilder类构建LiveData(让数据和Activity或Frament的生命周期绑定)不了解的小伙伴可以看我的这篇博客https://blog.csdn.net/xiatiandefeiyu/article/details/78663612

PagedList.Config config = new Builder()
                                        .setInitialLoadSizeHint(PAGE_SIZE)
                                        .setPageSize(PAGE_SIZE)
                                        .build();
        products = new LivePagedListBuilder(dataSourceFactory, config)
                        .setInitialLoadKey(1).setFetchExecutor(AppsExecutor.networkIO())
                        .build();

        return products;

 

第5步:实现DataSourceFactory工厂类创建加载数据的的DataSource(实现这个类就是为了加载数据,不管是从网络获取,还是从数据库、缓存、sd卡等获取)

public class ProductDataFactory extends DataSource.Factory<Integer, Product> {

    public MutableLiveData<ProductDataSource> datasourceLiveData = new MutableLiveData<>();

    @Override
    public DataSource<Integer, Product> create() {

        ProductDataSource dataSource = new ProductDataSource(Service.get());
        datasourceLiveData.postValue(dataSource);
        return dataSource;
    }
}

DataSource的实现类有ListDataSource、TiledDataSource、WrapperPositionalDataSource、LimitOffsetDataSource、PositionalDataSource、ItemKeyedDataSource、PageKeyedDataSource等你可以根据业务需要自己实现不同的类。我这里使用的是PageKeyedDataSource<Integer, Product>,需要你实现loadInitial、loadBefore、loadAfter三个方法,顾名思义loadInitial在第一次加载的时候调用也就是实现刷新在这个方法里实现,而加载更多在loadBeforeloadAfter方法中实现。

第6步:实现LiveData的的oberve的方法注册观察者回调

 mProductsViewModel.getProducts().observe(this, pagedList -> {
            mAdapter.submitList(pagedList);

第7步:在适配器中的onBindViewHolder方法中调用getItem(调用它就可以实现加载更多了)

ok,现在来分析源码是怎么进行的,首先明确几个重要类PagedListAdapter(负责适配数据,负责在getItem方法中实现继续加载),pageList类(持有数据的类),DataSource类(负责加载数据),LiveData(负责生命周期数据变化回调)。

咱们先从注册观察者跟进代码:

 public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        LifecycleBoundObserver existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && existing.owner != wrapper.owner) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        owner.getLifecycle().addObserver(wrapper);
    }

这里的getLifecycle得到的是 LifecycleRegistry类,进入:

 public void addObserver(@NonNull LifecycleObserver observer) {
        State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
        ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
        ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);

        if (previous != null) {
            return;
        }
        LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
        if (lifecycleOwner == null) {
            // it is null we should be destroyed. Fallback quickly
            return;
        }

        boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent;
        State targetState = calculateTargetState(observer);
        mAddingObserverCounter++;
        while ((statefulObserver.mState.compareTo(targetState) < 0
                && mObserverMap.contains(observer))) {
            pushParentState(statefulObserver.mState);
            statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState));
            popParentState();
            // mState / subling may have been changed recalculate
            targetState = calculateTargetState(observer);
        }

        if (!isReentrance) {
            // we do sync only on the top level.
            sync();
        }
        mAddingObserverCounter--;
    }

这段代码最重要的是statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState))这句话,添加观察者的时候,这个方法肯定会调用一次,最终会调用LifecycleBoundObserver的的onStateChanged方法

  public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            activeStateChanged(shouldBeActive());
        }

然后调用到父类的activeStateChanged的方法

 void activeStateChanged(boolean newActive) {
            if (newActive == mActive) {
                return;
            }
            // immediately set active state, so we'd never dispatch anything to inactive
            // owner
            mActive = newActive;
            boolean wasInactive = LiveData.this.mActiveCount == 0;
            LiveData.this.mActiveCount += mActive ? 1 : -1;
            if (wasInactive && mActive) {
                onActive();
            }
            if (LiveData.this.mActiveCount == 0 && !mActive) {
                onInactive();
            }
            if (mActive) {
                dispatchingValue(this);
            }
        }
    }

终于到重点了,看第9行的判断,第一次调用的时候肯定为true,然后会调用onActive的方法,废话了这么多,总结起来就是LiveDataobserve方法调用的时候会执行一次LiveDataonActive方法,那么这个LiveData在哪创建的,现在回到第四步,LiveData通过LivePagedListBuilder创建的,来看一下它的build方法

 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();
                        }
                    };

            @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 = dataSourceFactory.create();
                    mDataSource.addInvalidatedCallback(mCallback);

                    mList = new PagedList.Builder<>(mDataSource, config)
                            .setNotifyExecutor(notifyExecutor)
                            .setFetchExecutor(fetchExecutor)
                            .setBoundaryCallback(boundaryCallback)
                            .setInitialKey(initializeKey)
                            .build();
                } while (mList.isDetached());
                return mList;
            }
        }.getLiveData();
    }

最终通过ComputableLiveDatagetLiveData创建了LiveData,接着来看getLiveData方法,LiveData是在ComputableLiveData的构造方法中创建的

  public ComputableLiveData(@NonNull Executor executor) {
        mExecutor = executor;
        mLiveData = new LiveData<T>() {
            @Override
            protected void onActive() {
                mExecutor.execute(mRefreshRunnable);
            }
        };
    }

可以看到onActive方法在线程中执行了这面这段代码

  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());
        }
    };

它首先调用了自己的compute方法,如下:

   @Nullable Key initializeKey = initialLoadKey;
                if (mList != null) {
                    //noinspection unchecked
                    initializeKey = (Key) mList.getLastKey();
                }

                do {
                    if (mDataSource != null) {
                        mDataSource.removeInvalidatedCallback(mCallback);
                    }

                    mDataSource = dataSourceFactory.create();
                    mDataSource.addInvalidatedCallback(mCallback);

                    mList = new PagedList.Builder<>(mDataSource, config)
                            .setNotifyExecutor(notifyExecutor)
                            .setFetchExecutor(fetchExecutor)
                            .setBoundaryCallback(boundaryCallback)
                            .setInitialKey(initializeKey)
                            .build();
                } while (mList.isDetached());
                return mList;
            }

compute方法用来创建加载器Datasource以及数据源PageList(PagedListAdapter通过它获取数据),接着再看一下PageList的build方法

  public PagedList<Value> build() {
            // TODO: define defaults, once they can be used in module without android dependency
            if (mNotifyExecutor == null) {
                throw new IllegalArgumentException("MainThreadExecutor required");
            }
            if (mFetchExecutor == null) {
                throw new IllegalArgumentException("BackgroundThreadExecutor required");
            }

            //noinspection unchecked
            return PagedList.create(
                    mDataSource,
                    mNotifyExecutor,
                    mFetchExecutor,
                    mBoundaryCallback,
                    mConfig,
                    mInitialKey);
        }
    }

    /**

最终创建ContiguousPagedList

 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);
        }
    }

它的构造方法的倒数第三行里面实现了Datasource的的加载loadInitial方法在第一次加载数据(只有调用一次loadInitial,isInvalid()才返回true)。

 public void loadInitial(@NonNull LoadInitialParams<Integer> params, @NonNull LoadInitialCallback<Integer, Product> callback) 

这个方法的最后一个参数用来将你加载的数据结果返回给DataSource,并最终PageList进行数据保存

final void dispatchLoadInitial(@Nullable Key key, int initialLoadSize, int pageSize,
            boolean enablePlaceholders, @NonNull Executor mainThreadExecutor,
            @NonNull PageResult.Receiver<Value> receiver) {
        LoadInitialCallbackImpl<Key, Value> callback =
                new LoadInitialCallbackImpl<>(this, enablePlaceholders, receiver);
        loadInitial(new LoadInitialParams<Key>(initialLoadSize, enablePlaceholders), callback);

        // If initialLoad's callback is not called within the body, we force any following calls
        // to post to the UI thread. This constructor may be run on a background thread, but
        // after constructor, mutation must happen on UI thread.
        callback.mCallbackHelper.setPostExecutor(mainThreadExecutor);
    }

可以看到这个回调参数就是LoadInitialCallbackImpl类:

   public void onResult(@NonNull List<Value> data, @Nullable Key previousPageKey,
                @Nullable Key nextPageKey) {
            if (!mCallbackHelper.dispatchInvalidResultIfInvalid()) {
                mDataSource.initKeys(previousPageKey, nextPageKey);
                mCallbackHelper.dispatchResultToReceiver(new PageResult<>(data, 0, 0, 0));
            }
        }

这里又通过mCallbackHelper回调出去:

void dispatchResultToReceiver(final @NonNull PageResult<T> result) {
            Executor executor;
            synchronized (mSignalLock) {
                if (mHasSignalled) {
                    throw new IllegalStateException(
                            "callback.onResult already called, cannot call again.");
                }
                mHasSignalled = true;
                executor = mPostExecutor;
            }

            if (executor != null) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        mReceiver.onPageResult(mResultType, result);
                    }
                });
            } else {
                mReceiver.onPageResult(mResultType, result);
            }
        }
    }

下面又通过 PageResult.Receiver<T>回调出去,它的实现还是在ContiguousPagedList这类里面

                @NonNull PageResult<V> pageResult) {
            if (pageResult.isInvalid()) {
                detach();
                return;
            }
            if (isDetached()) {
                // No op, have detached
                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) {
                    // Because the ContiguousPagedList wasn't initialized with a last load position,
                    // initialize it to the middle of the initial load
                    mLastLoad =
                            pageResult.leadingNulls + pageResult.positionOffset + page.size() / 2;
                }
            } else if (resultType == PageResult.APPEND) {
                mStorage.appendPage(page, ContiguousPagedList.this);
            } else if (resultType == PageResult.PREPEND) {
                mStorage.prependPage(page, ContiguousPagedList.this);
            } else {
                throw new IllegalArgumentException("unexpected resultType " + resultType);
            }
             //省略若干行......
            }
        }
    };

这里做的工作只是判断是第一次加载,还是第二次存放数据(append),然后将数据保存到PagedStorage这个类中,现在再回到前面的compute中,它返回的TPageList<data>,而此时的PageList<data>已通过DataSource加载填充了数据,然后又调用了 LiveDatapostValue(value)方法,这个方法会触发(在activitty、frament处于生命周期活跃状态下)观察者的回调告诉观察者,我得数据改变了。你需要做点什么,也就是下面代码

mProductsViewModel.getProducts().observe(this, new Observer<PagedList<Product>>() {
            @Override
            public void onChanged(@Nullable PagedList<Product> products) {
                mAdapter.submitList(products);
            }
        });

onChanged方法会被回调,这里就做了一件事,向适配器中赋值PageList,然后适配器通过PageList获取数据,从而RecyclerView有了第一页的数据,当然这里源码还有DiffUtil做了数据的分析,从而提高数据刷新速度。这就是第一页数据怎么加载出来的,那么后面数据怎么触发来的呢?

上面说过它是通过getItem数据来每次加载更多的,那么来看一下它的代码

 protected T getItem(int position) {
        return mDiffer.getItem(position);
    }

继续跟入

 public T getItem(int index) {
        if (mPagedList == null) {
            if (mSnapshot == null) {
                throw new IndexOutOfBoundsException(
                        "Item count is zero, getItem() call is invalid");
            } else {
                return mSnapshot.get(index);
            }
        }

        mPagedList.loadAround(index);
        return mPagedList.get(index);
    }

看到了什么,调用了pageList的loadAround方法,肯定是去加载数据吗。来看一下

public void loadAround(int index) {
        mLastLoad = index + getPositionOffset();
        loadAroundInternal(index);

        mLowestIndexAccessed = Math.min(mLowestIndexAccessed, index);
        mHighestIndexAccessed = Math.max(mHighestIndexAccessed, index);

        /*
         * mLowestIndexAccessed / mHighestIndexAccessed have been updated, so check if we need to
         * dispatch boundary callbacks. Boundary callbacks are deferred until last items are loaded,
         * and accesses happen near the boundaries.
         *
         * Note: we post here, since RecyclerView may want to add items in response, and this
         * call occurs in PagedListAdapter bind.
         */
        tryDispatchBoundaryCallbacks(true);
    }

这里又调用了loadAroundInternal的方法,最终调用ContiguousPagedList类的loadAroundInternal方法

 protected void loadAroundInternal(int index) {
        int prependItems = mConfig.prefetchDistance - (index - mStorage.getLeadingNullCount());
        int appendItems = index + mConfig.prefetchDistance
                - (mStorage.getLeadingNullCount() + mStorage.getStorageCount());

        mPrependItemsRequested = Math.max(prependItems, mPrependItemsRequested);
        if (mPrependItemsRequested > 0) {
            schedulePrepend();
        }

        mAppendItemsRequested = Math.max(appendItems, mAppendItemsRequested);
        if (mAppendItemsRequested > 0) {
            scheduleAppend();
        }
    }

传过来的参数是当前RecyclerViewitem的position,其中prefetchDistance为预加载的数量,mStorage.getLeadingNullCount()默认为0,从这个方法代码可以推测出如果当前的预加载减掉索引如果大于0的话,会调用DataSourceloadBefore方法,如果当前的预加载数量+索引-目前已经加载的数量的话>0的话调用DataSourceloadAfter方法,从而实现数据的分页加载,所以我们只要实现loadBefore或loadAfter一个方法就好了,最后是添加的话就会回调

public void onPageAppended(int endPosition, int changedCount, int addedCount) {
        // consider whether to post more work, now that a page is fully appended

        mAppendItemsRequested = mAppendItemsRequested - changedCount - addedCount;
        mAppendWorkerRunning = false;
        if (mAppendItemsRequested > 0) {
            // not done appending, keep going
            scheduleAppend();
        }

        // finally dispatch callbacks, after append may have already been scheduled
        notifyChanged(endPosition, changedCount);
        notifyInserted(endPosition + changedCount, addedCount);
    }

方法来通知适配器数据改变了。

 

 

    原文作者:Android源码分析
    原文地址: https://blog.csdn.net/xiatiandefeiyu/article/details/85002160
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞