Android Jetpack组件之Paging Library源码篇

1,374 阅读6分钟

PS:原文首发于微信公众号:躬行之(jzman-blog)

阅读本文之前,可先阅读同系列 Android Jetpack 组件文章如下 :

接着上篇从 LiveData 的 observer 方法进入

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    assertMainThread("observe");
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignore
        return;
    }
    // 记住这个LifecycleBoundObserver,后面有用
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    if (existing != null && !existing.isAttachedTo(owner)) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    // 添加Observer,具体是LifecycleBoundObserver
    owner.getLifecycle().addObserver(wrapper);
}

继续查看 LifecycleRegistry 的 addObserver 方法,源码如下:

@Override
public void addObserver(@NonNull LifecycleObserver observer) {
    // ...
    ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
    while ((statefulObserver.mState.compareTo(targetState) < 0
            && mObserverMap.contains(observer))) {
        pushParentState(statefulObserver.mState);
        // ObserverWithState的dispatchEvent方法
        statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState));
        popParentState();
        // mState / subling may have been changed recalculate
        targetState = calculateTargetState(observer);
    }
    // ...
}

static class ObserverWithState {
    State mState;
    LifecycleEventObserver mLifecycleObserver;
    ObserverWithState(LifecycleObserver observer, State initialState) {
        mLifecycleObserver = Lifecycling.lifecycleEventObserver(observer);
        mState = initialState;
    }
    void dispatchEvent(LifecycleOwner owner, Event event) {
        State newState = getStateAfter(event);
        mState = min(mState, newState);
        // 实际调用的是LifecycleBoundObserver的onStateChanged方法
        mLifecycleObserver.onStateChanged(owner, event);
        mState = newState;
    }
}

继续查看 LiveData 的内部类 LifecycleBoundObserver 的 onStateChanged 方法,源码如下:

// 方法
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
    // Lifecycle组件监听的生命周期方法回调
    if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
        removeObserver(mObserver);
        return;
    }
    // 查看源码shouldBeActive方法知,只要Activity的状态在ON_START之后就返回true
    activeStateChanged(shouldBeActive());
}

// activeStateChanged方法
void activeStateChanged(boolean newActive) {
    // mActive默认false,所以不成立
    // 如果第二次执行mActive为false,则不继续处理,最终的结果就是不会回到onChanged方法
    if (newActive == mActive) {
        return;
    }
    // 第一次执行完后mActive的值将被设置为true
    mActive = newActive;
    // 只要有活跃观察者,也就是组件的状态是START或RESUME时mActiveCount就不等于0,wasInactive为true
    boolean wasInactive = LiveData.this.mActiveCount == 0;
    // mActiveCount大于0
    LiveData.this.mActiveCount += mActive ? 1 : -1;
    // wasInactive和mActive都为true,执行
    if (wasInactive && mActive) {
        onActive();
    }
    // mActiveCount大于0 ,fasle && false,不执行
    if (LiveData.this.mActiveCount == 0 && !mActive) {
        onInactive();
    }
    // mActive为true
    if (mActive) {
        //分发处理ObserverWrapper,也就是添加进去的观察者LifecycleBoundObserver
        dispatchingValue(this);
    }
}

看到这里至少就可以解释为什么在 PagedList 初始加载数据时会回调 onChanged 方法,而在加载下一页数据时不再回调 onChanged 方法了,

继续查看 LiveData 的方法 dispatchingValue 方法,源码如下:

// dispatchingValue方法
void dispatchingValue(@Nullable ObserverWrapper initiator) {
    // ...
    for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
        // 遍历获取Observer处理
        considerNotify(iterator.next().getValue());
        //...
    }
    // ...
}

// considerNotify方法
private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) {
        return;
    }
    // 观察者不处于活跃状态,不通知观察者
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    // 通知观察者
    observer.mObserver.onChanged((T) mData);
}

至此,将结果回调给具体的观察者。

从 mPagedList 的创建开始,创建 mPagedList 代码如下:

// LiveData
private LiveData<PagedList<DataBean.ResultsBean>> mPagedList = new LivePagedListBuilder<>(factory,config)
        .build();

继续查看关键方法 build,源代码如下:


// build方法
public LiveData<PagedList<Value>> build() {
    return create(mInitialLoadKey, mConfig, mBoundaryCallback, mDataSourceFactory,
            ArchTaskExecutor.getMainThreadExecutor(), mFetchExecutor);
}

// create方法
private static <Key, Value> LiveData<PagedList<Value>> create(
      
    return new ComputableLiveData<PagedList<Value>>(fetchExecutor) {
        @Nullable
        private PagedList<Value> mList;
        @Nullable
        private DataSource<Key, Value> mDataSource;
        // DataSource失效回调
        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);
                }

                mDataSource = dataSourceFactory.create();
                mDataSource.addInvalidatedCallback(mCallback);
                // 创建PagedList,PageList是如何创建存储的且看下文
                mList = new PagedList.Builder<>(mDataSource, config)
                        .setNotifyExecutor(notifyExecutor)
                        .setFetchExecutor(fetchExecutor)
                        .setBoundaryCallback(boundaryCallback)
                        .setInitialKey(initializeKey)
                        .build();
            } while (mList.isDetached());//DataSource无效时,还是使用以前的数据,DataSource有效是执行一次返回PagedList
            return mList;
        }
    }.getLiveData();
}

使用 getLiveData 获取 LiveData 数据,其中 LiveData 数据的更新是在一个名为 mRefreshRunnable 的 Ruuable 中更新的,在 mRefreshRunnable 中会调用上面的 compute 方法生成 PagedList,然后使用 LiveData 的 postValue 方法更新 PageList 数据到 LiveData 中,源码如下:

final Runnable mRefreshRunnable = new Runnable() {
    // ...
    try {
        T value = null;
        while (mInvalid.compareAndSet(true, false)) {
            computed = true;
            // 生成PagedList
            value = compute();
        }
        if (computed) {
            // 更新LiveData
            mLiveData.postValue(value);
        }
    } finally {
        // release compute lock
        mComputing.set(false);
    }
    
    // ...
};

到此为止,LiveData<PagedList> 从创建到更新就分析完了。

接着上文继续看一下 PagedList 是如何生成的,PagedList 创建的关键源码如下:

// PagedList的创建
mList = new PagedList.Builder<>(mDataSource, config)
    .setNotifyExecutor(notifyExecutor)
    .setFetchExecutor(fetchExecutor)
    .setBoundaryCallback(boundaryCallback)
    .setInitialKey(initializeKey)
    .build();
    

// 继续查看build方法,调用了PagedList的create方法
public PagedList<Value> build() {
    // ...
    return PagedList.create(
            mDataSource,
            mNotifyExecutor,
            mFetchExecutor,
            mBoundaryCallback,
            mConfig,
            mInitialKey);
}

// 真正创建PagedList
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) {
    // PageKeyedDataSource继承ContiguousDataSource,dataSource.isContiguous()为true
    if (dataSource.isContiguous() || !config.enablePlaceholders) {
        int lastLoad = ContiguousPagedList.LAST_LOAD_UNSPECIFIED;
        // 使用PageKeyedDataSource不会执行
        if (!dataSource.isContiguous()) {
            //noinspection unchecked
            dataSource = (DataSource<K, T>) ((PositionalDataSource<T>) dataSource)
                    .wrapAsContiguousWithoutPlaceholders();
            if (key != null) {
                lastLoad = (Integer) key;
            }
        }
    
        // 创建并返回ContiguousPagedList,也就是PagedList
        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);
    }
}

继续查看 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.dispatchLoadInitial(key,
            mConfig.initialLoadSizeHint,
            mConfig.pageSize,
            mConfig.enablePlaceholders,
            mMainThreadExecutor,
            mReceiver);
    // ...
}

这里调用了抽象类 ContiguousDataSource 的 dispatchLoadInitial 方法,查看各类具体的实现类 PageKeyedDataSource 里面实现的具体的 dispatchLoadInitial 方法,源码如下:

@Override
final void dispatchLoadInitial(@Nullable Key key, int initialLoadSize, int pageSize,
        boolean enablePlaceholders, @NonNull Executor mainThreadExecutor,
        @NonNull PageResult.Receiver<Value> receiver) {
    // 创建了LoadInitialCallback,也就是自定义DataSource中回调从网络加载数据时使用的callback
    LoadInitialCallbackImpl<Key, Value> callback =
            new LoadInitialCallbackImpl<>(this, enablePlaceholders, receiver);
    // 自定义DataSource需要实现的loadInitial方法,在这里完成回调
    loadInitial(new LoadInitialParams<Key>(initialLoadSize, enablePlaceholders), callback);
    // 设置callback的回调执行在主线程
    callback.mCallbackHelper.setPostExecutor(mainThreadExecutor);
}

到此为止,还不知道回调过去的数据时怎么保存的,所以继续查看 callback 的具体实现及回调,源码如下:

static class LoadInitialCallbackImpl<Key, Value> extends LoadInitialCallback<Key, Value> {
    final LoadCallbackHelper<Value> mCallbackHelper;
    private final PageKeyedDataSource<Key, Value> mDataSource;
    private final boolean mCountingEnabled;
    // LoadInitialCallbackImpl构造方法
    LoadInitialCallbackImpl(@NonNull PageKeyedDataSource<Key, Value> dataSource,
            boolean countingEnabled, @NonNull PageResult.Receiver<Value> receiver) {
        // 这里需记住ResultType为PageResult.INIT,也就是初始加载数据时候数据的类型,后文中遇到PageResult.INIT
        mCallbackHelper = new LoadCallbackHelper<>(
                dataSource, PageResult.INIT, null, receiver);
        mDataSource = dataSource;
        mCountingEnabled = countingEnabled;
    }

    // 这个onResult回调显然不是PageKeyedDataSource设置数据时使用,应该是使用PositionalDataSource使用的,这里略过
    @Override
    public void onResult(@NonNull List<Value> data, int position, int totalCount,
            @Nullable Key previousPageKey, @Nullable Key nextPageKey) {
        if (!mCallbackHelper.dispatchInvalidResultIfInvalid()) {
            LoadCallbackHelper.validateInitialLoadParams(data, position, totalCount);

            // setup keys before dispatching data, so guaranteed to be ready
            mDataSource.initKeys(previousPageKey, nextPageKey);

            int trailingUnloadedCount = totalCount - position - data.size();
            if (mCountingEnabled) {
                mCallbackHelper.dispatchResultToReceiver(new PageResult<>(
                        data, position, trailingUnloadedCount, 0));
            } else {
                mCallbackHelper.dispatchResultToReceiver(new PageResult<>(data, position));
            }
        }
    }

    // 这个onResult回调是PageKeyedDataSource设置数据时使用的
    @Override
    public void onResult(@NonNull List<Value> data, @Nullable Key previousPageKey,
            @Nullable Key nextPageKey) {
        // DataSource有效时dispatchInvalidResultIfInvalid返回false
        if (!mCallbackHelper.dispatchInvalidResultIfInvalid()) {
            // 初始化在loadInitial中设置的previousPageKey, nextPageKey,应该是用来区分加载上一页还是加载下一页
            mDataSource.initKeys(previousPageKey, nextPageKey);
            // 分发回调过来的数据data
            mCallbackHelper.dispatchResultToReceiver(new PageResult<>(data, 0, 0, 0));
        }
    }
}

这里已经拿到了从网络加载的数据,继续查看 dispatchResultToReceiver 方法,看数据时如何被处理的,其源码如下:

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

    // 如果executor为null,因为在上面的分析中,这里的回调executor被赋值并运行在主线程
    if (executor != null) {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                // 数据继续回调
                mReceiver.onPageResult(mResultType, result);
            }
        });
    } else {// 非主线程直接回调
        mReceiver.onPageResult(mResultType, result);
    }
}

这里继续查看 onPageResult 的具体实现,查看源码知这个过程在 ContiguousPagedList 中实现,其源码具体如下:

PageResult.Receiver<V> mReceiver = new PageResult.Receiver<V>() {
    @AnyThread
    @Override
    public void onPageResult(@PageResult.ResultType int resultType,
            @NonNull PageResult<V> pageResult) {
        // ...
        List<V> page = pageResult.page;
        // 前面在初始化加载数据,创建LoadCallbackHelper时,数据状态是PageResult.INIT
        // 也就是初始化调用loadInitial的时候
        if (resultType == PageResult.INIT) {
            // 1.保存数据到PagedStorage
            // 2.最终调用RecyclerView.Adapte的notifyItemRangeInserted(position, count)方法,position为0
            mStorage.init(pageResult.leadingNulls, page, pageResult.trailingNulls,
                    pageResult.positionOffset, ContiguousPagedList.this);
            // ...
        } else {
            // ...
            // 创建LoadCallbackHelper时,数据状态是PageResult.APPEND
            // 也就是调用loadAfter的时候
            if (resultType == PageResult.APPEND) {
                if (skipNewPage && !trimFromFront) {
                    // don't append this data, drop it
                    mAppendItemsRequested = 0;
                    mAppendWorkerState = READY_TO_FETCH;
                } else {
                    // 1.保存数据到PagedStorage
                    // 2.最终回调loadAfter
                    mStorage.appendPage(page, ContiguousPagedList.this);
                }
            // 创建LoadCallbackHelper时,数据状态是PageResult.PREPEND
            // 也就是调用loadBefore的时候
            } else if (resultType == PageResult.PREPEND) {
                if (skipNewPage && trimFromFront) {
                    // don't append this data, drop it
                    mPrependItemsRequested = 0;
                    mPrependWorkerState = READY_TO_FETCH;
                } else {
                    // 1.保存数据到PagedStorage
                    // 2.最终回调loadBefore
                    mStorage.prependPage(page, ContiguousPagedList.this);
                }
            } else {
                throw new IllegalArgumentException("unexpected resultType " + resultType);
            }

            // ...
    }
};

到此 PagedList 的创建以及保存过程就结束了,之前从 observer 开始分析了 PagedList 的数据回调,会调用 onChanged 方法,在该方法里面为 RecyclerVIew 的 Adaptyer 设置数据,代码如下:

mViewModel.getPagedList().observe(this, new Observer<PagedList<DataBean.ResultsBean>>() {
    @Override
    public void onChanged(PagedList<DataBean.ResultsBean> dataBeans) {
        // 设置要显示的数据,只会在初始化的时候回调一次
        // 如果已经有显示列表,则会在后台线程中计算数据差异,并通知数据更新
        mAdapter.submitList(dataBeans);
    }
});

上面提到如果以及存在显示列表,则会在后台线程中计算数据差异并通知数据更新,那么这个后台线程在哪呢,mAdapter 的 submitList 方法最终调用下面的 submitList 方法,里面通过 AsyncDifferConfig 获取后台线程执行数据的差异计算,这个后台线程池如果不指定则是一个固定大小为 2 的线程池,源码如下:

@SuppressWarnings("ReferenceEquality")
public void submitList(@Nullable final PagedList<T> pagedList,
        @Nullable final Runnable commitCallback) {
    // ...
    final PagedList<T> oldSnapshot = mSnapshot;
    final PagedList<T> newSnapshot = (PagedList<T>) pagedList.snapshot();
    // 获取后台线程池执行计算数据差异的线程,并在里面将计算结果回调到主线程
    mConfig.getBackgroundThreadExecutor().execute(new Runnable() {
        @Override
        public void run() {
        
            final DiffUtil.DiffResult result;
            result = PagedStorageDiffHelper.computeDiff(
                    oldSnapshot.mStorage,
                    newSnapshot.mStorage,
                    mConfig.getDiffCallback());

            // 将计算结果回调到主线程,然后通知数据集更新,进而通知RecyclerView更新数据
            mMainThreadExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    if (mMaxScheduledGeneration == runGeneration) {
                        latchPagedList(pagedList, newSnapshot, result,
                                oldSnapshot.mLastLoad, commitCallback);
                    }
                }
            });
        }
    });
}

继续查看 latchPagedList 源码,这里将会进行数据的更新操作以及读取数据,其源码如下:

void latchPagedList(
        @NonNull PagedList<T> newList,
        @NonNull PagedList<T> diffSnapshot,
        @NonNull DiffUtil.DiffResult diffResult,
        int lastAccessIndex,
        @Nullable Runnable commitCallback) {
    // ...
    // 更新mPageList并通知RecyclerView更新数据
    PagedStorageDiffHelper.dispatchDiff(mUpdateCallback,
            previousSnapshot.mStorage, newList.mStorage, diffResult);

    newList.addWeakCallback(diffSnapshot, mPagedListCallback);

    if (!mPagedList.isEmpty()) {
        // 读取数据
        mPagedList.loadAround(Math.max(0, Math.min(mPagedList.size() - 1, newPosition)));
    }

    onCurrentListChanged(previousSnapshot, mPagedList, commitCallback);
}

其中 dispatchDiff 方法最终会调用 dispatchLastEvent 方法,源码如下:

public void dispatchLastEvent() {
    if (mLastEventType == TYPE_NONE) {
        return;
    }
    switch (mLastEventType) {
        case TYPE_ADD:
            mWrapped.onInserted(mLastEventPosition, mLastEventCount);
            break;
        case TYPE_REMOVE:
            mWrapped.onRemoved(mLastEventPosition, mLastEventCount);
            break;
        case TYPE_CHANGE:
            mWrapped.onChanged(mLastEventPosition, mLastEventCount, mLastEventPayload);
            break;
    }
    mLastEventPayload = null;
    mLastEventType = TYPE_NONE;
}

mWrapped 是一个 ListUpdateCallback,而 AdapterListUpdateCallback 实现了该接口,并在里面通知 RectclerView 的 Adapter 进行数据更新。

其中在 latchPagedList 方法中会调用 PagedList 的 loadAround 方法来获取数据,也就是使用在使用 PageKeyedDataSource 的时候实现的 loadBefore 和 loadAfter 方法,这里就不贴源码了。

Page Library 的分页加载流程就分析完了,看源码至少知道了 onChanged 方法只在初始化的时候只回调过一次,但是却能够不断加载下一页数据的原因。