个人博客
http://www.milovetingting.cn
Jetpack学习-Paging Paging是什么 分页库可一次加载和显示一小块数据。按需载入部分数据会减少网络带宽和系统资源的使用量。
简单使用 引入Paging 在需要引入Paging模块的build.gradle中配置
1 2 def paging_version = "2.1.0" implementation "androidx.paging:paging-runtime:$paging_version"
定义Bean 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 public class Student { private String id; private String name; private String gender; public String getId () { return id; } public void setId (String id) { this .id = id; } public String getName () { return name; } public void setName (String name) { this .name = name; } public String getGender () { return gender; } public void setGender (String gender) { this .gender = gender; } @Override public boolean equals (Object o) { if (this == o) { return true ; } if (o == null || getClass() != o.getClass()) { return false ; } Student student = (Student) o; return id.equals(student.id) && name.equals(student.name) && gender.equals(student.gender); } @RequiresApi(api = Build.VERSION_CODES.KITKAT) @Override public int hashCode () { return Objects.hash(id, name, gender); } }
需要重写equals
和hashCode
方法,后面比较数据时会用到
定义DataSource 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class StudentDataSource extends PositionalDataSource <Student> { @Override public void loadInitial (@NonNull LoadInitialParams params, @NonNull LoadInitialCallback<Student> callback) { callback.onResult(getStudents(0 , Config.SIZE), 0 , 1000 ); } @Override public void loadRange (@NonNull LoadRangeParams params, @NonNull LoadRangeCallback<Student> callback) { callback.onResult(getStudents(params.startPosition, params.loadSize)); } private List<Student> getStudents (int startPosition, int pageSize) { List<Student> list = new ArrayList <>(); for (int i = startPosition; i < startPosition + pageSize; i++) { Student student = new Student (); student.setId("ID:" + i); student.setName("名称:" + i); student.setGender("性别:" + i); list.add(student); } return list; } }
定义一个类继承自PositionalDataSource
,这是一个固定大小的数据源。这里只作演示,具体业务可以根据实际情况修改。
在这个类中定义获取数据的方法getStudents
,然后重写loadInitial
,loadRange
方法
定义DataSourceFactory 1 2 3 4 5 6 7 8 public class StudentDataSourceFactory extends DataSource .Factory<Integer, Student> { @NonNull @Override public DataSource<Integer, Student> create () { StudentDataSource dataSource = new StudentDataSource (); return dataSource; } }
定义ViewModel 1 2 3 4 5 6 7 8 9 10 11 12 13 public class StudentViewModel extends ViewModel { private final LiveData<PagedList<Student>> listLiveData; public StudentViewModel () { StudentDataSourceFactory factory = new StudentDataSourceFactory (); this .listLiveData = new LivePagedListBuilder <Integer, Student>(factory, Config.SIZE).build(); } public LiveData<PagedList<Student>> getListLiveData () { return listLiveData; } }
定义Adapter 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 public class RecyclerPagingAdapter extends PagedListAdapter <Student, RecyclerPagingAdapter.RecyclerViewHolder> { private static DiffUtil.ItemCallback<Student> DIFF_STUDENT = new DiffUtil .ItemCallback<Student>() { @Override public boolean areItemsTheSame (@NonNull Student oldItem, @NonNull Student newItem) { return oldItem.getId() == newItem.getId(); } @Override public boolean areContentsTheSame (@NonNull Student oldItem, @NonNull Student newItem) { return oldItem.equals(newItem); } }; public RecyclerPagingAdapter () { super (DIFF_STUDENT); } @NonNull @Override public RecyclerViewHolder onCreateViewHolder (@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_paging, parent,false ); return new RecyclerViewHolder (view); } @Override public void onBindViewHolder (@NonNull RecyclerViewHolder holder, int position) { Student student = getItem(position); if (student == null ) { holder.tvId.setText("加载中" ); holder.tvName.setText("加载中" ); holder.tvGender.setText("加载中" ); } else { holder.tvId.setText(student.getId()); holder.tvName.setText(student.getName()); holder.tvGender.setText(student.getGender()); } } public static class RecyclerViewHolder extends RecyclerView .ViewHolder { TextView tvId; TextView tvName; TextView tvGender; public RecyclerViewHolder (@NonNull View itemView) { super (itemView); tvId = itemView.findViewById(R.id.id); tvName = itemView.findViewById(R.id.name); tvGender = itemView.findViewById(R.id.gender); } } }
显示数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class PagingActivity extends AppCompatActivity { RecyclerView recyclerView; RecyclerPagingAdapter adapter; StudentViewModel viewModel; @Override protected void onCreate (@Nullable Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_paging); recyclerView = findViewById(R.id.rv); adapter = new RecyclerPagingAdapter (); viewModel = new ViewModelProvider (this , new ViewModelProvider .NewInstanceFactory()).get(StudentViewModel.class); viewModel.getListLiveData().observe(this , new Observer <PagedList<Student>>() { @Override public void onChanged (PagedList<Student> students) { adapter.submitList(students); } }); recyclerView.setAdapter(adapter); recyclerView.setLayoutManager(new LinearLayoutManager (this )); } }
原理 分析Paging,首先从获取数据开始: viewModel.getListLiveData()
。
1 2 3 public LiveData<PagedList<Student>> getListLiveData () { return listLiveData; }
listLiveData在构造方法中赋值
1 2 3 4 public StudentViewModel () { StudentDataSourceFactory factory = new StudentDataSourceFactory (); this .listLiveData = new LivePagedListBuilder <Integer, Student>(factory, Config.SIZE).build(); }
通过LivePagedListBuilder的build
方法赋值
1 2 3 public LiveData<PagedList<Value>> build () { return create(this .mInitialLoadKey, this .mConfig, this .mBoundaryCallback, this .mDataSourceFactory, ArchTaskExecutor.getMainThreadExecutor(), this .mFetchExecutor); }
调用create
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 private static <Key, Value> LiveData<PagedList<Value>> create (@Nullable final Key initialLoadKey, @NonNull final Config config, @Nullable final BoundaryCallback boundaryCallback, @NonNull final 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 InvalidatedCallback mCallback = new InvalidatedCallback () { public void onInvalidated () { invalidate(); } }; protected PagedList<Value> compute () { Key initializeKey = initialLoadKey; if (this .mList != null ) { initializeKey = this .mList.getLastKey(); } do { if (this .mDataSource != null ) { this .mDataSource.removeInvalidatedCallback(this .mCallback); } this .mDataSource = dataSourceFactory.create(); this .mDataSource.addInvalidatedCallback(this .mCallback); this .mList = (new androidx .paging.PagedList.Builder(this .mDataSource, config)).setNotifyExecutor(notifyExecutor).setFetchExecutor(fetchExecutor).setBoundaryCallback(boundaryCallback).setInitialKey(initializeKey).build(); } while (this .mList.isDetached()); return this .mList; } }).getLiveData(); }
实例化了一个ComputableLiveData对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public ComputableLiveData (@NonNull Executor executor) { this .mInvalid = new AtomicBoolean (true ); this .mComputing = new AtomicBoolean (false ); this .mRefreshRunnable = new Runnable () { @WorkerThread public void run () { boolean computed; do { computed = false ; if (ComputableLiveData.this .mComputing.compareAndSet(false , true )) { try { Object value; for (value = null ; ComputableLiveData.this .mInvalid.compareAndSet(true , false ); value = ComputableLiveData.this .compute()) { computed = true ; } if (computed) { ComputableLiveData.this .mLiveData.postValue(value); } } finally { ComputableLiveData.this .mComputing.set(false ); } } } while (computed && ComputableLiveData.this .mInvalid.get()); } }; this .mInvalidationRunnable = new Runnable () { @MainThread public void run () { boolean isActive = ComputableLiveData.this .mLiveData.hasActiveObservers(); if (ComputableLiveData.this .mInvalid.compareAndSet(false , true ) && isActive) { ComputableLiveData.this .mExecutor.execute(ComputableLiveData.this .mRefreshRunnable); } } }; this .mExecutor = executor; this .mLiveData = new LiveData <T>() { protected void onActive () { ComputableLiveData.this .mExecutor.execute(ComputableLiveData.this .mRefreshRunnable); } }; }
在ComputableLiveData
的构造方法中,定义了一个mRefreshRunnable
,当LiveData
的onActive
方法回调时,就会执行mRefreshRunnable
RefreshRunnable
的run
方法,先执行compute
然后会通过ComputableLiveData.this.mLiveData.postValue(value)
刷新
先来看compute
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 protected PagedList<Value> compute () { Key initializeKey = initialLoadKey; if (this .mList != null ) { initializeKey = this .mList.getLastKey(); } do { if (this .mDataSource != null ) { this .mDataSource.removeInvalidatedCallback(this .mCallback); } this .mDataSource = dataSourceFactory.create(); this .mDataSource.addInvalidatedCallback(this .mCallback); this .mList = (new androidx .paging.PagedList.Builder(this .mDataSource, config)).setNotifyExecutor(notifyExecutor).setFetchExecutor(fetchExecutor).setBoundaryCallback(boundaryCallback).setInitialKey(initializeKey).build(); } while (this .mList.isDetached()); return this .mList; }
在这个方法里面调用dataSourceFactory.create()
这个实现在我们定义的类中
1 2 3 4 public DataSource<Integer, Student> create () { StudentDataSource dataSource = new StudentDataSource (); return dataSource; }
然后通过PagedList.Builder.build()对mList进行赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public PagedList<Value> build () { if (mNotifyExecutor == null ) { throw new IllegalArgumentException ("MainThreadExecutor required" ); } if (mFetchExecutor == null ) { throw new IllegalArgumentException ("BackgroundThreadExecutor required" ); } return PagedList.create( mDataSource, mNotifyExecutor, mFetchExecutor, mBoundaryCallback, mConfig, mInitialKey); }
调用PagedList.create
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 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()) { 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 ); } }
看下ContiguousPagedList
的构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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; }
调用dispatchLoadInitial
1 2 3 4 5 6 7 8 9 10 11 void dispatchLoadInitial (@Nullable Integer position, int initialLoadSize, int pageSize, boolean enablePlaceholders, @NonNull Executor mainThreadExecutor, @NonNull PageResult.Receiver<Value> receiver) { final int convertPosition = position == null ? 0 : position; mSource.dispatchLoadInitial(false , convertPosition, initialLoadSize, pageSize, mainThreadExecutor, receiver); }
调用dispatchLoadInitial
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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); callback.mCallbackHelper.setPostExecutor(mainThreadExecutor); }
调用loadInitial
,这里调用到了我们定义的StudentDataSource
1 2 3 public void loadInitial (@NonNull LoadInitialParams params, @NonNull LoadInitialCallback<Student> callback) { callback.onResult(getStudents(0 , Config.SIZE), 0 , 1000 ); }
调用onResult
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public void onResult (@NonNull List<T> data, int position) { if (!mCallbackHelper.dispatchInvalidResultIfInvalid()) { if (position < 0 ) { throw new IllegalArgumentException ("Position must be non-negative" ); } if (data.isEmpty() && position != 0 ) { throw new IllegalArgumentException ( "Initial result cannot be empty if items are present in data set." ); } if (mCountingEnabled) { throw new IllegalStateException ("Placeholders requested, but totalCount not" + " provided. Please call the three-parameter onResult method, or" + " disable placeholders in the PagedList.Config" ); } mCallbackHelper.dispatchResultToReceiver(new PageResult <>(data, position)); } }
调用dispatchResultToReceiver
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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); } }
compute
暂时分析到这里,后面先不作过多深入
compute
执行后,会执行postValue
,这样在Activity中的回调就会执行
1 2 3 public void onChanged (PagedList<Student> students) { adapter.submitList(students); }
会调用submitList
1 2 3 public void submitList (@Nullable PagedList<T> pagedList) { this .mDiffer.submitList(pagedList); }
调用submitList
1 2 3 public void submitList (@Nullable PagedList<T> pagedList) { this .submitList(pagedList, (Runnable)null ); }
调用submitList
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 public void submitList (@Nullable final PagedList<T> pagedList, @Nullable final Runnable commitCallback) { if (pagedList != null ) { if (this .mPagedList == null && this .mSnapshot == null ) { this .mIsContiguous = pagedList.isContiguous(); } else if (pagedList.isContiguous() != this .mIsContiguous) { throw new IllegalArgumentException ("AsyncPagedListDiffer cannot handle both contiguous and non-contiguous lists." ); } } final int runGeneration = ++this .mMaxScheduledGeneration; if (pagedList == this .mPagedList) { if (commitCallback != null ) { commitCallback.run(); } } else { PagedList<T> previous = this .mSnapshot != null ? this .mSnapshot : this .mPagedList; if (pagedList == null ) { int removedCount = this .getItemCount(); if (this .mPagedList != null ) { this .mPagedList.removeWeakCallback(this .mPagedListCallback); this .mPagedList = null ; } else if (this .mSnapshot != null ) { this .mSnapshot = null ; } this .mUpdateCallback.onRemoved(0 , removedCount); this .onCurrentListChanged(previous, (PagedList)null , commitCallback); } else if (this .mPagedList == null && this .mSnapshot == null ) { this .mPagedList = pagedList; pagedList.addWeakCallback((List)null , this .mPagedListCallback); this .mUpdateCallback.onInserted(0 , pagedList.size()); this .onCurrentListChanged((PagedList)null , pagedList, commitCallback); } else { if (this .mPagedList != null ) { this .mPagedList.removeWeakCallback(this .mPagedListCallback); this .mSnapshot = (PagedList)this .mPagedList.snapshot(); this .mPagedList = null ; } if (this .mSnapshot != null && this .mPagedList == null ) { final PagedList<T> oldSnapshot = this .mSnapshot; final PagedList<T> newSnapshot = (PagedList)pagedList.snapshot(); this .mConfig.getBackgroundThreadExecutor().execute(new Runnable () { public void run () { final DiffResult result = PagedStorageDiffHelper.computeDiff(oldSnapshot.mStorage, newSnapshot.mStorage, AsyncPagedListDiffer.this .mConfig.getDiffCallback()); AsyncPagedListDiffer.this .mMainThreadExecutor.execute(new Runnable () { public void run () { if (AsyncPagedListDiffer.this .mMaxScheduledGeneration == runGeneration) { AsyncPagedListDiffer.this .latchPagedList(pagedList, newSnapshot, result, oldSnapshot.mLastLoad, commitCallback); } } }); } }); } else { throw new IllegalStateException ("must be in snapshot state to diff" ); } } } }
这个方法里会执行onCurrentListChanged
进行通知更新。
Paging的原理先分析到这里。什么?这也太简单了吧,根本没有讲清楚具体的流程啊!你是不是不会原理啊!!!嗯~~~说对了,目前还只是理解到这里,后续再补充(手动尴尬)!