Android分頁(yè)止剖,Java代碼官方Paging框架使用簡(jiǎn)析

轉(zhuǎn)載請(qǐng)標(biāo)明出處http://www.reibang.com/p/b9c7a6e3e8d2

前言:最近有個(gè)需求腺阳,要考慮到系統(tǒng)資源以及網(wǎng)絡(luò)請(qǐng)求的效率,需要做一個(gè)類(lèi)似于現(xiàn)在市面上那種列表頁(yè)面可以往上滑動(dòng)不斷加載item的效果穿香,想著自己寫(xiě)邏輯亭引,對(duì)控件recycleview的滑動(dòng)到底部的事件進(jìn)行判定之后請(qǐng)求數(shù)據(jù),再去對(duì)adapter進(jìn)行數(shù)據(jù)的判斷增加然后視圖刷新皮获。一籮筐下來(lái)覺(jué)得好麻煩啊焙蚓,就去Google了一下,發(fā)現(xiàn)官方提供了Paging庫(kù)來(lái)處理這個(gè)場(chǎng)景。由于這個(gè)框架也是運(yùn)用到LiveData购公,不熟悉的朋友可以先看一下之前LiveData的文章萌京。

1.Paging

Paging是一個(gè)官方提供的分頁(yè)庫(kù)。使用這個(gè)庫(kù)宏浩,我們只需要關(guān)心數(shù)據(jù)知残,分頁(yè)和視圖是不需要我們?nèi)リP(guān)心的,這個(gè)庫(kù)會(huì)幫我們實(shí)現(xiàn)比庄。在這個(gè)庫(kù)求妹,最重要的就是關(guān)鍵組件是PagedList類(lèi)。而且一般來(lái)說(shuō)佳窑,這個(gè)庫(kù)都會(huì)搭配著RXjava2或者LiveData來(lái)使用制恍,友好的處理控件和數(shù)據(jù)的生命周期。本文的例子將會(huì)使用LiveData神凑。

2.Paging的配合

2.1.數(shù)據(jù)

列表數(shù)據(jù)的來(lái)源净神,可分為本地?cái)?shù)據(jù)和網(wǎng)絡(luò)數(shù)據(jù),本地?cái)?shù)據(jù)官方是建議使用Room持久庫(kù)來(lái)整理數(shù)據(jù)的耙厚,而網(wǎng)絡(luò)數(shù)據(jù)强挫,可以使用自己定義的數(shù)據(jù)源工廠(chǎng),本文例子也將使用自定義數(shù)據(jù)源工廠(chǎng)薛躬。

22.界面

分頁(yè)展示,這個(gè)庫(kù)需要搭配recyclerview來(lái)進(jìn)行展示呆细,recyclerview也會(huì)有Paging提供的特殊adapter類(lèi)型宝。

3.使用

二話(huà)不多說(shuō),項(xiàng)目依賴(lài)走起絮爷。

   implementation "androidx.paging:paging-runtime:2.1.2"
   testImplementation "androidx.paging:paging-common:2.1.2"
   implementation "androidx.paging:paging-rxjava2:2.1.2"

分頁(yè)列表的實(shí)現(xiàn)趴酣,界面由recyclerview來(lái)實(shí)現(xiàn),這里recyclerview要注意坑夯,要繼承Paging庫(kù)提供的PagedListAdapter類(lèi)岖寞,這是實(shí)現(xiàn)分頁(yè)效果的關(guān)鍵。

public class AppleAdapter extends PagedListAdapter<apple, AppleAdapter.MyViewHolder> {

使用這個(gè)類(lèi)我們不需要再重寫(xiě)getItemCount方法柜蜈,PagedListAdapter自己重寫(xiě)了getItemCount仗谆,我們只需要通過(guò)設(shè)置DiffUtil來(lái)使得它可以對(duì)數(shù)據(jù)差異進(jìn)行判斷∈缏模可以通過(guò)item特有屬性的對(duì)別或者item的整個(gè)對(duì)象的對(duì)比來(lái)得出差異隶垮,從而決定是否要更新到列表中去。

public AppleAdapter() {
        super(DIFF_CALLBACK);
    }

public static final DiffUtil.ItemCallback<apple> DIFF_CALLBACK = new DiffUtil.ItemCallback<apple>() {
        @Override
        public boolean areItemsTheSame(@NonNull apple oldApple, @NonNull apple newApple) {
            // User properties may have changed if reloaded from the DB, but ID is fixed
            return oldApple.getId() == newApple.getId();
        }

        @Override
        public boolean areContentsTheSame(@NonNull apple oldApple, @NonNull apple newApple) {
            // NOTE: if you use equals, your object must properly override Object#equals()
            // Incorrectly returning false here will result in too many animations.
            return newApple.equals(newApple);
        }
    };

其次就是數(shù)據(jù)了秘噪,這里我自己定義了數(shù)據(jù)源工廠(chǎng)類(lèi)狸吞。配合了LiveData進(jìn)行使用。先來(lái)看一下數(shù)據(jù)源工廠(chǎng)類(lèi)。

 private class MyAppleSourceFactory extends DataSource.Factory<Integer, apple> {
        private MutableLiveData<MyAppleSource> sourceMutableLiveData = new MutableLiveData<>();
        private MyAppleSource source;

        @NonNull
        @Override
        public DataSource<Integer, apple> create() {
            source = new MyAppleSource();
            //查看Google的文檔也沒(méi)看明白這個(gè)liveData是為什么
            //但是猜測(cè)可能是想利用liveData對(duì)-生命周期進(jìn)行監(jiān)聽(tīng),有懂的朋友可以評(píng)論不吝賜教蹋偏。
            sourceMutableLiveData.postValue(source);
            return source;
        }
    }

    private class MyAppleSource extends ItemKeyedDataSource<Integer, apple> {

        @Override
        public void loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialCallback callback) {
            List<apple> items = getMoreMyApple(0);
            callback.onResult(items);
        }

        @Override
        public void loadAfter(@NonNull LoadParams params, @NonNull LoadCallback callback) {
            List<apple> items = getMoreMyApple((Integer) params.key);
            callback.onResult(items);
        }

        @Override
        public void loadBefore(@NonNull LoadParams params, @NonNull LoadCallback callback) {
        }

        @NonNull
        @Override
        public Integer getKey(@NonNull Apple item) {
            return item.getId();
        }
    }

工廠(chǎng)類(lèi)內(nèi)部實(shí)例化了一個(gè)Source類(lèi)便斥,這個(gè)類(lèi)我這里是繼承了ItemKeyedDataSource,他是規(guī)定整個(gè)分頁(yè)是由item的某一個(gè)屬性去獲取數(shù)據(jù)的威始。官方還提供了其余2個(gè)Source類(lèi):PageKeyedDataSource枢纠,PositionalDataSource。解決各自的特定場(chǎng)景字逗,需要的朋友可以自己Google了解一下京郑。

然后看一下控件和工廠(chǎng)類(lèi)數(shù)據(jù)的初始化,我這里是用了Viewmodel來(lái)實(shí)現(xiàn)demo的葫掉。

public LiveData<PagedList<Apple>> appleMutableLiveData;

public void initMyApple() {
        MyAppleSourceFactory appleSourceFactory = new MyAppleSourceFactory();
        myAppleSource = appleSourceFactory.create();
        //pageList的LiveData由activity這個(gè)UI層去進(jìn)行監(jiān)聽(tīng)些举。
        appleMutableLiveData = new LivePagedListBuilder(appleSourceFactory, 10).build();
    }

最后看一下Activity,

 appleMyBinding = DataBindingUtil.setContentView(this, R.layout.activity_apple_my);
        //蘋(píng)果列表初始化俭厚,其實(shí)就是正常的recyclerview的管理器布局器設(shè)置
        initRVApple();
        myAppleViewModel = ViewModelProviders.of(this).get(AppleViewModel.class);
        //PagedList的LiveData初始化
        myAppleViewModel.initMyApple();
        myAppleViewModel.appleMutableLiveData.observe(this, appleVOS -> {
            //停止加載户魏,通知回調(diào)
            myAppleViewModel.invalidateDataSource();
            //游戲列表數(shù)據(jù)變化監(jiān)聽(tīng),其實(shí)這個(gè)操作相當(dāng)于向adapter傳遞數(shù)據(jù)的過(guò)程。
            //這也是一個(gè)LiveData連接PagedListAdapter的過(guò)程
            //PagedListAdapter會(huì)自動(dòng)處理分頁(yè)差異更新
            appleAdapter.submitList(appleVOS);
        });

這里關(guān)鍵其實(shí)是appleAdapter.submitList(appleVOS)挪挤。是由于這個(gè)方法叼丑,recyclerview和數(shù)據(jù)搭上了線(xiàn)。
到這里代碼就寫(xiě)的差不多了扛门。activity中recyclerview的初始化鸠信,數(shù)據(jù)源工廠(chǎng)類(lèi)的自定義,viewmodel中的數(shù)據(jù)源工廠(chǎng)類(lèi)初始化论寨。通過(guò)這一系列操作就可以實(shí)現(xiàn)分頁(yè)的效果星立。
在不知道他的實(shí)現(xiàn)原理的情況下,我們大膽猜測(cè)一下葬凳,整個(gè)流程是這樣的绰垂,在ViewModel中LiveData通常是去通過(guò)model層獲取數(shù)據(jù)設(shè)置數(shù)據(jù)到LiveData對(duì)象中的,也就是當(dāng)分頁(yè)開(kāi)始請(qǐng)求數(shù)據(jù)的時(shí)候火焰,數(shù)據(jù)來(lái)源于工廠(chǎng)類(lèi)劲装,它實(shí)際上會(huì)走Source類(lèi)的loadInitial方法設(shè)置數(shù)據(jù),之后界面每次數(shù)據(jù)不夠顯示了昌简,就會(huì)調(diào)用loadAfter的方法去設(shè)置數(shù)據(jù)占业。實(shí)際中g(shù)etMoreMyApple也是對(duì)應(yīng)了Model層,是一個(gè)關(guān)于數(shù)據(jù)的網(wǎng)絡(luò)請(qǐng)求方法江场,根據(jù)數(shù)據(jù)的id去請(qǐng)求數(shù)據(jù)纺酸。數(shù)據(jù)設(shè)置到LiveData中了,通過(guò)Adapter.submitList綁定到界面址否。那么關(guān)鍵就在于餐蔬,裝有PagedList的LiveData是如何得到的碎紊。咱們來(lái)看一下源碼。

4.原理簡(jiǎn)析

官方寫(xiě)法是樊诺,在activity新建時(shí)viewmodel實(shí)例化仗考,一起調(diào)用LivePagedListBuilder而獲得一個(gè)LiveData對(duì)象,這個(gè)對(duì)象中包含了PagedList對(duì)象词爬。這個(gè)LiveData是如何獲得秃嗜,咱們一層一層剝開(kāi)看看。

appleMutableLiveData = new LivePagedListBuilder(appleSourceFactory, 10).build();

這個(gè)build到底做了什么可以得到一個(gè)LiveData顿膨,點(diǎn)開(kāi)源碼瞧瞧锅锨。

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

他返回的是一個(gè)create方法執(zhí)行得到的結(jié)果×滴郑看看create方法必搞。

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

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

create方法返回的則是一個(gè)ComputableLiveData類(lèi)的實(shí)例化對(duì)象的getLiveData的值,看看這個(gè)ComputableLiveData類(lèi)構(gòu)造方法以及這個(gè)getLiveData方法囊咏。

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

   @NonNull
    public LiveData<T> getLiveData() {
        return mLiveData;
    }

原來(lái)這一系列下來(lái)是一個(gè)LiveData對(duì)象實(shí)例化以及返回的過(guò)程恕洲。而且當(dāng)這個(gè)liveData啟用的時(shí)候會(huì)在線(xiàn)程池中運(yùn)行一個(gè)子線(xiàn)程。咱們看看這個(gè)子線(xiàn)程了干了些什么梅割。

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

這個(gè)子線(xiàn)程中對(duì)LiveData進(jìn)行了postValue霜第,這下子就清楚,這就是build方法返回的裝有PagedList的liveData數(shù)據(jù)變化户辞,UI也會(huì)變化了泌类。而這個(gè)postValue的值來(lái)源于compute抽象方法的,咱們回頭看看在LivePagedListBuilder中實(shí)例化的ComputableLiveData對(duì)象的compute方法具體是怎么實(shí)現(xiàn)的底燎。

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

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

他post的value原來(lái)是來(lái)自于這個(gè)方法新建的PagedList對(duì)象末誓,而他的值就是來(lái)源于dataSourceFactory的mDataSource對(duì)象的。這個(gè)factory對(duì)象就是我們build的時(shí)候傳入的自定義數(shù)據(jù)源工廠(chǎng)類(lèi)了书蚪。然后再來(lái)看看這個(gè)PagedList是怎么build生成一個(gè)PagedList對(duì)象的。

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

由PagedList類(lèi)create方法實(shí)例化的迅栅,往下看殊校。

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

看著源碼,這個(gè)source對(duì)象通過(guò)一系列判斷最后會(huì)轉(zhuǎn)為兩種PagedList類(lèi)读存,不過(guò)ContiguousDataSource比較特別为流,我們點(diǎn)進(jìn)去看看。

ContiguousDataSource<Integer, T> wrapAsContiguousWithoutPlaceholders() {
        return new ContiguousWithoutPlaceholdersWrapper<>(this);
    }

ContiguousWithoutPlaceholdersWrapper(
                @NonNull PositionalDataSource<Value> source) {
            mSource = source;
        }
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;
    }

TiledPagedList(@NonNull PositionalDataSource<T> dataSource,
            @NonNull Executor mainThreadExecutor,
            @NonNull Executor backgroundThreadExecutor,
            @Nullable BoundaryCallback<T> boundaryCallback,
            @NonNull Config config,
            int position) {
        super(new PagedStorage<T>(), mainThreadExecutor, backgroundThreadExecutor,
                boundaryCallback, config);
        mDataSource = dataSource;

        final int pageSize = mConfig.pageSize;
        mLastLoad = position;

        if (mDataSource.isInvalid()) {
            detach();
        } else {
            final int firstLoadSize =
                    (Math.max(mConfig.initialLoadSizeHint / pageSize, 2)) * pageSize;

            final int idealStart = position - firstLoadSize / 2;
            final int roundedPageStart = Math.max(0, idealStart / pageSize * pageSize);

            mDataSource.dispatchLoadInitial(true, roundedPageStart, firstLoadSize,
                    pageSize, mMainThreadExecutor, mReceiver);
        }
    }

這兩個(gè)PagedList方法邏輯相似一上來(lái)就會(huì)對(duì)DataSource再判斷让簿,然后決定是否走PositionalDataSource的dispatchLoadInitial方法敬察。
這個(gè)方法看著好眼熟,會(huì)讓人想起自定義的source類(lèi)不是嗎尔当。咱們往下看莲祸。

 final void dispatchLoadInitial(boolean acceptCount,
            int requestedStartPosition, int requestedLoadSize, int pageSize,
            @NonNull Executor mainThreadExecutor, @NonNull PageResult.Receiver<T> receiver) {
        LoadInitialCallbackImpl<T> callback =
                new LoadInitialCallbackImpl<>(this, acceptCount, pageSize, receiver);

        LoadInitialParams params = new LoadInitialParams(
                requestedStartPosition, requestedLoadSize, pageSize, acceptCount);
        loadInitial(params, 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);
    }

這個(gè)方法蹂安,直接就調(diào)用我們自定義source類(lèi)的loadInitial方法了。就可以拿到自定義請(qǐng)求方法得到的PagedList了锐帜,還有l(wèi)oadAfter和loadBefore的調(diào)用過(guò)程這里就不再深入了田盈。

接下來(lái)來(lái)看一下,這個(gè)source中的關(guān)于數(shù)據(jù)加載時(shí)機(jī)的回調(diào)缴阎,是怎么配合PagedListAdapter的允瞧。看看Adapter蛮拔。

 public AppleAdapter() {
        super(DIFF_CALLBACK);
    }

protected PagedListAdapter(@NonNull DiffUtil.ItemCallback<T> diffCallback) {
        mDiffer = new AsyncPagedListDiffer<>(this, diffCallback);
        mDiffer.addPagedListListener(mListener);
    }

 private final AsyncPagedListDiffer.PagedListListener<T> mListener =
            new AsyncPagedListDiffer.PagedListListener<T>() {
        @Override
        public void onCurrentListChanged(
                @Nullable PagedList<T> previousList, @Nullable PagedList<T> currentList) {
            PagedListAdapter.this.onCurrentListChanged(currentList);
            PagedListAdapter.this.onCurrentListChanged(previousList, currentList);
        }
    };

這里先記住述暂,它內(nèi)部會(huì)實(shí)例化一個(gè)AsyncPagedListDiffer對(duì)象并且賦予回調(diào)以及設(shè)置PagedListListener。
然后我們來(lái)看一下PagedListAdapter的submit方法建炫。因?yàn)槭且蕾?lài)這個(gè)方法實(shí)現(xiàn)數(shù)據(jù)和界面的綁定的畦韭。

  appleAdapter.submitList(apples);
  public void submitList(@Nullable PagedList<T> pagedList) {
        mDiffer.submitList(pagedList);
    }

發(fā)現(xiàn)是調(diào)用了AsyncPagedListDiffer這個(gè)類(lèi)的submitList方法。往里面深處翻發(fā)現(xiàn)

public void submitList(@Nullable final PagedList<T> pagedList,
            @Nullable final Runnable commitCallback) {

這里有個(gè)關(guān)鍵方法踱卵,onCurrentListChanged(previous, null, commitCallback);

private void onCurrentListChanged(
            @Nullable PagedList<T> previousList,
            @Nullable PagedList<T> currentList,
            @Nullable Runnable commitCallback) {
        for (PagedListListener<T> listener : mListeners) {
            listener.onCurrentListChanged(previousList, currentList);
        }
        if (commitCallback != null) {
            commitCallback.run();
        }
    }

實(shí)際上他就是調(diào)用了上面PagedListListener的onCurrentListChanged廊驼,走的是PagedListAdapter的onCurrentListChanged方法的⊥锷埃看了一下源碼注釋和翻了一下文檔妒挎,這是一個(gè)當(dāng)前PagedList更新時(shí)調(diào)用的兩個(gè)方法。

到這里就差不多知道整個(gè)流程了西饵。通過(guò)LivePagedListBuilder拿到工廠(chǎng)類(lèi)的Source酝掩,通過(guò)一系列回調(diào)走到我們自定義loadInitial方法,由此拿到PageList對(duì)象眷柔,將一個(gè)PagedList對(duì)象傳遞給Adapter期虾,當(dāng)頁(yè)面變化,調(diào)用AsyncPagedListDiffer的onCurrentListChanged驯嘱,就會(huì)觸發(fā)Adapter的onCurrentListChanged镶苞。

5.總結(jié)

剛開(kāi)始用的時(shí)候,由于都不知道其原理鞠评,只能先一邊看文檔一邊寫(xiě)個(gè)demo茂蚓,功能實(shí)現(xiàn)了,就是搞不明白為什么要寫(xiě)factory為什么要寫(xiě)source剃幌,為什么是LivePagedListBuilder去build拿到LiveData的聋涨,為什么通過(guò)submitList就可以讓數(shù)據(jù)綁定到控件,然后隨著控件滑動(dòng)就可以觸發(fā)source的loadAfter了负乡。通過(guò)翻看源碼結(jié)合注釋以及翻看文檔牍白。終于懂得一二,總算知道了為什么可以通過(guò)這樣一個(gè)流程實(shí)現(xiàn)一個(gè)分頁(yè)的效果了抖棘。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末茂腥,一起剝皮案震驚了整個(gè)濱河市狸涌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌础芍,老刑警劉巖杈抢,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異仑性,居然都是意外死亡惶楼,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)诊杆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)歼捐,“玉大人,你說(shuō)我怎么就攤上這事晨汹”ⅲ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵淘这,是天一觀(guān)的道長(zhǎng)剥扣。 經(jīng)常有香客問(wèn)我,道長(zhǎng)铝穷,這世上最難降的妖魔是什么钠怯? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮曙聂,結(jié)果婚禮上晦炊,老公的妹妹穿的比我還像新娘。我一直安慰自己宁脊,他們只是感情好断国,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著榆苞,像睡著了一般稳衬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上坐漏,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天宋彼,我揣著相機(jī)與錄音,去河邊找鬼仙畦。 笑死,一個(gè)胖子當(dāng)著我的面吹牛音婶,可吹牛的內(nèi)容都是我干的慨畸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼衣式,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼寸士!你這毒婦竟也來(lái)了檐什?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤弱卡,失蹤者是張志新(化名)和其女友劉穎乃正,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體婶博,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瓮具,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了凡人。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片名党。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖挠轴,靈堂內(nèi)的尸體忽然破棺而出传睹,到底是詐尸還是另有隱情,我是刑警寧澤岸晦,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布欧啤,位于F島的核電站,受9級(jí)特大地震影響启上,放射性物質(zhì)發(fā)生泄漏邢隧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一碧绞、第九天 我趴在偏房一處隱蔽的房頂上張望府框。 院中可真熱鬧,春花似錦讥邻、人聲如沸迫靖。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)系宜。三九已至,卻和暖如春发魄,著一層夾襖步出監(jiān)牢的瞬間盹牧,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工励幼, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留汰寓,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓苹粟,卻偏偏與公主長(zhǎng)得像有滑,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子嵌削,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容