What are the benefits of using Paging
- Advantage 1: the paging library can be more easily in the application of RecyclerView step by step and elegant loading data;
- Benefits 2: Data requests consume less network bandwidth and fewer system resources;
- Benefit 3: The application continues to respond quickly to user input even during data updates and refreshes;
- Benefit four: however much waste, display how much to use how much;
The use of the Paging
- Role 1: DataSource (is a DataSource, including various forms, e.g. Room source, PositionalDataSource source, PageKeyedDataSource source, ItemKeyedDataSource source)
- Role 2: PagedList (UIModel data layer, get data source via Factory)
- Role 3: PagedAdapter (note that it is not a RecycleView adapter, but a PagedListAdapter supporting Paging)
- RecycleView role 4: RecycleView (RecycleView, only setAdapter binding adapter is PagedAdapter)
public class Student {
private String id;
private String name;
private String sex;
public String getId(a) {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName(a) {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex(a) {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
// Compare the function
@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) &&
sex.equals(student.sex);
}
// Compare the function
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public int hashCode(a) {
returnObjects.hash(id, name, sex); }}Copy the code
Role 1 Data source
Data source is the source of data, which can be multiple sources, such as “network data”, “local data”, “database data”.
public class StudentDataSource extends PositionalDataSource<Student> {
/** * this function is used to load the first page of data. * Figuratively speaking, when we first open the page, we need to call back to this method to get the data. *@param params
* @param callback
*/
@Override
public void loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialCallback<Student> callback) {
// @1 Data source @2 location @3 total size
callback.onResult(getStudents(0, Flag.SIZE), 0.1000);
}
/** * This method is called when data needs to be loaded while sliding after initialization. *@param params
* @param callback
*/
@Override
public void loadRange(@NonNull LoadRangeParams params, @NonNull LoadRangeCallback<Student> callback) {
@2 size(size = 1)
callback.onResult(getStudents(params.startPosition, params.loadSize));
}
/** * It can be understood that this is the data source, the source of the data (database, file, web server response, etc.) *@param startPosition
* @param pageSize
* @return* /
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 number is:" + i);
student.setName("My name :" + i);
student.setSex("My gender :" + i);
list.add(student);
}
returnlist; }}Copy the code
Role 2 Data factory
Create a factory to manage data sources. Why have a factory, in addition to creating data sources, for subsequent extensions
/** * data factory */
public class StudentDataSourceFactory extends DataSource.Factory<Integer.Student> {
@NonNull
@Override
public DataSource<Integer, Student> create(a) {
StudentDataSource studentDataSource = new StudentDataSource();
returnstudentDataSource; }}Copy the code
Role 3 Data model
A data model is actually a ViewModel, which is used to manage data
PagedList: Data retrieved from the data source is ultimately hosted by PagedList. The way to think about PagedList is, it’s just a collection of data on a page. Each page requested is a new PagedList object.
public class StudentViewModel extends ViewModel {
@1 listLiveData = @1 listLiveData
private final LiveData<PagedList<Student>> listLiveData;
public StudentViewModel(a) {
StudentDataSourceFactory factory = new StudentDataSourceFactory();
// Initialize the ViewModel
this.listLiveData = new LivePagedListBuilder<Integer, Student>(factory, Flag.SIZE).build();
}
// TODO exposes data
public LiveData<PagedList<Student>> getListLiveData() {
returnlistLiveData; }}Copy the code
Role 4 Adapter
This Adapter is a RecyclerView Adapter. However, we use paging to achieve the effect of RecyclerView paging loading, can not inherit the Adapter of RecyclerView directly, but need to inherit the PagedListAdapter.
LiveData observed data, induced data to the adapter, the adapter bind RecyclerView, then the RecyclerView list data changed
public class RecyclerPagingAdapter extends PagedListAdapter<Student.RecyclerPagingAdapter.MyRecyclerViewHolder> {
// TODO compares the behavior
private static DiffUtil.ItemCallback<Student> DIFF_STUDNET = new
DiffUtil.ItemCallback<Student>() {
// It is a unique content, ID
@Override
public boolean areItemsTheSame(@NonNull Student oldItem, @NonNull Student newItem) {
return oldItem.getId().equals(newItem.getId());
}
// Compare the object itself
@Override
public boolean areContentsTheSame(@NonNull Student oldItem, @NonNull Student newItem) {
returnoldItem.equals(newItem); }};protected RecyclerPagingAdapter(a) {
super(DIFF_STUDNET);
}
@NonNull
@Override
public MyRecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, null);
return new MyRecyclerViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MyRecyclerViewHolder holder, int position) {
Student student = getItem(position);
// When the item view is displayed and the paging library is still loading data, I show that the Id is loading
if (null == student) {
holder.tvId.setText("Id loading");
holder.tvName.setText("Name in load");
holder.tvSex.setText("Sex loading");
} else{ holder.tvId.setText(student.getId()); holder.tvName.setText(student.getName()); holder.tvSex.setText(student.getSex()); }}// Item optimization ViewHolder
public static class MyRecyclerViewHolder extends RecyclerView.ViewHolder {
TextView tvId;
TextView tvName;
TextView tvSex;
public MyRecyclerViewHolder(View itemView) {
super(itemView);
tvId = itemView.findViewById(R.id.tv_id); // ID
tvName = itemView.findViewById(R.id.tv_name); / / name
tvSex = itemView.findViewById(R.id.tv_sex); / / gender}}}Copy the code
Display the results
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
RecyclerPagingAdapter recyclerPagingAdapter;
StudentViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recycle_view);
recyclerPagingAdapter = new RecyclerPagingAdapter();
// The latest version initializes the viewModel
viewModel = new ViewModelProvider(this.new ViewModelProvider.NewInstanceFactory())
.get(StudentViewModel.class);
// LiveData observer updates
viewModel.getListLiveData().observe(this.new Observer<PagedList<Student>>() {
@Override
public void onChanged(PagedList<Student> students) {
// Update adapter data hererecyclerPagingAdapter.submitList(students); }}); recyclerView.setAdapter(recyclerPagingAdapter); recyclerView.setLayoutManager(new LinearLayoutManager(this)); }}Copy the code
The roles of Paging
- A source of data;
- Datasource. Factory: The Factory class provides an instance of a DataSource that you can use to customize a DataSource.
- PagedList: data center, request loading data from DataSource as needed, and pass the data to PagedListAdapter;
- PagedListAdapter: data adapter, here in addition to play a general interface load adapter, more important is according to the sliding display of coordinates, play to determine when required to load data to the PagedList;
- Diffutil. ItemCallback: Checks whether the data has changed to determine whether the interface is updated;
Data source detail
DataSource is an abstract class, but we cannot directly inherit it and implement its subclasses. However, the Paging library provides three subclasses of it for us to inherit implementations for different scenarios:
PositionalDataSource: the data is loaded from a specific location. Key is the location information of type Integer, and T is the Value. Let’s say you start with 1200 entries in your database and add 20 entries.
ItemKeyedDataSource<Key, Value> The loading of target data depends on the information of a specific item, that is, the Key field contains the information of item. For example, the data of the N+1 item needs to be loaded according to the information of the NTH item, and the ID of the NTH item needs to be passed in the parameter transmission. This scenario mostly occurs in the request of the forum class to apply comment information.
PageKeyedDataSource<Key, Value> : applies to scenarios where target data requests data based on page information, that is, the Key field is page-related information. For example, the parameters of the requested data contain information like the number of next/Pervious pages.
Source code analysis of Paging
The relationship between class
Abstract class DataSource<Key, Value> :
Abstract class ItemKeyedDataSource<Key, Value> :
Abstract Class PageKeyedDataSource<Key, Value> :
Abstract Class PositionalDataSource: The data source subclass we just used
DataSource has three subclasses:
PageKeyedDataSource: If the page needs to implement the previous page, the next page, the requested Token needs to be passed to the next page using ItemKeyedDataSource: The PositionalDataSource is used when the program needs to fetch the next data based on the previous data information (ID) : the data page needs to be retrieved from any location in the data store; For example, a request might return 20 data items starting with position 1200
Of course, the analysis starts at the place where the data is taken. The execution of Paging components starts from the creation of LiveData. Our source code analysis also starts from the creation of LiveData to explore the logic behind Paging.
Initialization work
Private Final LiveData<PagedList> listLiveData; “How this variable is created:
public StudentViewModel(a) {
StudentDataSourceFactory factory = new StudentDataSourceFactory();
this.listLiveData = new LivePagedListBuilder<Integer, Student>(factory, 20)
.build();
}
Copy the code
Click to enter build function analysis:
@NonNull
@SuppressLint("RestrictedApi")
public LiveData<PagedList<Value>> build() {
return create(mInitialLoadKey, mConfig, mBoundaryCallback, mDataSourceFactory,
ArchTaskExecutor.getMainThreadExecutor(), mFetchExecutor);
}
Copy the code
Enter create function analysis:
Use LivePagedListBuilder to configure Factory and Config, and then call Build to create the instance. Create LiveData directly by calling create () in the build method
@AnyThread
@NonNull
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) {
// Note: Create the ComputableLiveData abstract class here
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(a) { invalidate(); }};// Notice that we are overriding the compute method here, which is the PagedList
we need
@Override
protected PagedList<Value> compute(a) {
@Nullable Key initializeKey = initialLoadKey;
if(mList ! =null) {
//noinspection unchecked
initializeKey = (Key) mList.getLastKey();
}
do {
if(mDataSource ! =null) {
mDataSource.removeInvalidatedCallback(mCallback);
}
// Create the DataSource from the Factory passed in Builder
mDataSource = dataSourceFactory.create();
mDataSource.addInvalidatedCallback(mCallback);
/ / create the PagedList
mList = new PagedList.Builder<>(mDataSource, config)
.setNotifyExecutor(notifyExecutor)
.setFetchExecutor(fetchExecutor)
.setBoundaryCallback(boundaryCallback)
.setInitialKey(initializeKey)
.build();
} while (mList.isDetached());
return mList;
}
}.getLiveData();
}
Copy the code
In create (), which returns an instance of ComputableLiveData directly, some major operations are performed in the compute overridden by the ComputableLiveData instance:
Call Factory create () to create a DataSource instance. Create and return PagedList instance; Pagedlist.build () & pagedList.create () is the following code (details);
mList = new PagedList.Builder<>(mDataSource, config)
.setNotifyExecutor(notifyExecutor)
.setFetchExecutor(fetchExecutor)
.setBoundaryCallback(boundaryCallback)
.setInitialKey(initializeKey)
.build();
Copy the code
public PagedList<Value> build(a) {
// 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);
}
Copy the code
The PagedList creation process calls pagedList.create () in pagedlist.build (), so the actual creation takes place in create() :
private static <K, T> PagedList<T> create(...). {
if(dataSource.isContiguous() || ! config.enablePlaceholders) { ......return new ContiguousPagedList<>(contigDataSource,
notifyExecutor,
fetchExecutor,
boundaryCallback,
config,
key,
lastLoad);
} else {
return newTiledPagedList<>((PositionalDataSource<T>) dataSource, notifyExecutor, fetchExecutor, boundaryCallback, config, (key ! =null)? (Integer) key :0); }}Copy the code
See from the above code according to the conditions (dataSource. IsContiguous () | |! Config. EnablePlaceholders) of different create ContiguousPagedList and TiledPagedList respectively, actually this is to distinguish the type of the above three custom DataSource (three sources), If PositionalDataSource creates TiledPagedList, the other returns ContiguousPagedList, and we check isspagedlist () methods in the three DataSource in turn:
PositionalDataSource class:
@Overrideboolean isContiguous() {
return false;
}
Copy the code
ItemKeyedDataSource and PageKeyedDataSource both inherit and access to a ContiguousDataSource, and only check the class of ContiguousDataSource:
@Overrideboolean isContiguous() {
return true;
}
Copy the code
Back again, starting with LivePagelistBuilder.build:
ComputableLiveData (LiveData) : ComputableLiveData (LiveData) : ComputableLiveData (LiveData) : ComputableLiveData (LiveData) : ComputableLiveData (LiveData) : ComputableLiveData See what logic is executed in the Runnable interface:
public ComputableLiveData(@NonNull Executor executor) {
mExecutor = executor;
mLiveData = new LiveData<T>() {
@Override
protected void onActive(a) { mExecutor.execute(mRefreshRunnable); }}; }Copy the code
final Runnable mRefreshRunnable = new Runnable() {
@WorkerThread
@Override
public void run(a) {
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;
// compute() is executed here; function
// Compuet was called to create the PagedList
value = compute();
}
if (computed) {
// Set the LiveData valuemLiveData.postValue(value); }}finally {
// release compute lock
mComputing.set(false); }}... }while(computed && mInvalid.get()); }};Copy the code
The PagedList is created by calling the compute () method of ComputableLiveData in mRefreshRunnable, so the Value here is the PagedList, and then the PagedList is initialized for mLiveData.
If you are careful, notice that the last line of the create () method above calls getLiveData() to obtain the LIveData created in the ComputableLiveData constructor:
@SuppressWarnings("WeakerAccess")
@NonNull
public LiveData<T> getLiveData(a) {
return mLiveData;
}
Copy the code
At this point, LiveData is finally created
Data loading
We use the spagedlist as a starting point
When we customize the implementation of ItemKeySource, the PagedList created is actually a ContiguousPagedList, check the source code of the constructor of 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); } mShouldTrim = mDataSource.supportsPageDropping()&& mConfig.maxSize ! = Config.MAX_SIZE_UNBOUNDED; }Copy the code
DispatchLoadInitial () for ItermKeyDataSource
Perform some logic in the constructor, so keep tracing the code:
First point: create PagedStorage instance, mainly according to the sliding position to show whether to continue loading data
The second point: call the DataSource. DispatchLoadInitial method, this time using the ItermKeyDataSource dispatchLoadInitial method
@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);
}
Copy the code
ItermKeyDataSource dispatchLoadInitial () calls loadInitial() in the dispatchLoadInitial () method. Thus, the initialization data of the Paging component is loaded
Data display work
After loading the data in the loadInitial () of the custom ItemDataSource, callback.onResult(it? .data!! .datas!!) Method, where callback is the LoadInitialCallback implementation LoadInitialCallbackImpl, In the onResult () method call again LoadCallbackHelper. DispatchResultToReceiver ()
static class LoadInitialCallbackImpl<Key.Value> extends LoadInitialCallback<Key.Value> {
final LoadCallbackHelper<Value> mCallbackHelper;
private final PageKeyedDataSource<Key, Value> mDataSource;
private final boolean mCountingEnabled;
LoadInitialCallbackImpl(@NonNull PageKeyedDataSource<Key, Value> dataSource,
boolean countingEnabled, @NonNull PageResult.Receiver<Value> receiver) {
mCallbackHelper = new LoadCallbackHelper<>(
dataSource, PageResult.INIT, null, receiver);
mDataSource = dataSource;
mCountingEnabled = countingEnabled;
}
@Override
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)); }}Copy the code
Mp5: LoadCallbackHelper 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;
}
if(executor ! =null) {
executor.execute(new Runnable() {
@Override
public void run(a) { mReceiver.onPageResult(mResultType, result); }}); }else{ mReceiver.onPageResult(mResultType, result); }}Copy the code
In dispatchResultToReceiver () method, called PageResult) Receiver. OnPageResult () method, The mReceiver here is in the call mDataSource. DispatchLoadInitial () is introduced to the last parameter, the realization of his anonymous created in ContiguousPagedList:
final PageResult.Receiver<T> mReceiver; // mSignalLock protects mPostExecutor, and mHasSignalled
private final Object mSignalLock = new Object();
private Executor mPostExecutor = null;
private boolean mHasSignalled = false;
LoadCallbackHelper(@NonNull DataSource dataSource, @PageResult.ResultType int resultType,
@Nullable Executor mainThreadExecutor, @NonNull PageResult.Receiver<T> receiver) {
mDataSource = dataSource;
mResultType = resultType;
mPostExecutor = mainThreadExecutor;
mReceiver = receiver;
}
Copy the code
ContiguousPagedList:
private PageResult.Receiver<V> mReceiver = new PageResult.Receiver<V>() {
// Creation thread for initial synchronous load, otherwise main thread
// Safe to access main thread only state - no other thread has reference during construction
@AnyThread
@Override
public void onPageResult(@PageResult.ResultType int resultType,
@NonNull PageResult<V> pageResult) {
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); }}}};Copy the code
In the onPageResult () method, the three data types of the PageResult correspond to the three methods of the ItemKeyDataSource:
INIT loadBefore: Indicates the initialization state pageresult. PREPEND loadAfter: indicates the initialization state pageresult. APPEND
This parse initializes with a callback of type pageresult.init that calls the INIT () method of PagedStorage:
mStorage.init(pageResult.leadingNulls, page, pageResult.trailingNulls,
pageResult.positionOffset, ContiguousPagedList.this);
Copy the code
void init(int leadingNulls,
@NonNull List<T> page, int trailingNulls, int positionOffset,
@NonNull Callback callback) {
init(leadingNulls, page, trailingNulls, positionOffset);
callback.onInitialized(size());
}
Copy the code
Another init () method is first called in the init () method to record where it was loaded and save the loaded data, then callback.oninitialized () is called, and notifyInserted () is called in the onInitialzed () method, Iterate over onInserted () of the mCallbacks callback in notifyInserted ()
interface Callback {
void onInitialized(int count);
void onPagePrepended(int leadingNulls, int changed, int added);
void onPageAppended(int endPosition, int changed, int added);
void onPagePlaceholderInserted(int pageIndex);
void onPageInserted(int start, int count);
void onPagesRemoved(int startOfDrops, int count);
void onPagesSwappedToPlaceholder(int startOfDrops, int count);
void onEmptyPrepend(a);
void onEmptyAppend(a);
}
Copy the code
Continue to trace the source:
//ContiguousPagedList:
public void onInitialized(int count) {
notifyInserted(0, count);
}
PagedList:
void notifyInserted(int position, int count) {
if(count ! =0) {
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
Callback callback = mCallbacks.get(i).get();
if(callback ! =null) { callback.onInserted(position, count); }}}}// Interface of PagedList :public abstract void onInserted(int position, int count);
Copy the code
The above source code, let us understand:
One: the loaded data is saved in PagedStorage, and the loading location information is recorded
Callback () notifies callback.oninserted () of the number and position of data changes after the load is complete
At last there was light:
So where did CallBack come from? Where does it need to register callbacks, think about where changes in data location are useful, where are preferences handled based on position and count? The answer is in the PagedListAdapter and we’re finally going to see the PagedListAdapter
PagedListAdapter
Of course, we can also simply trace the code to the PagedListAdapter
AsyncPagedListDiffer:
public AsyncPagedListDiffer(@NonNull ListUpdateCallback listUpdateCallback, @NonNull AsyncDifferConfig<T> config) {
class NamelessClass_1 extends Callback {
NamelessClass_1() {
}
public void onInserted(int position, int count) {
AsyncPagedListDiffer.this.mUpdateCallback.onInserted(position, count);
}
public void onRemoved(int position, int count) {
AsyncPagedListDiffer.this.mUpdateCallback.onRemoved(position, count);
}
public void onChanged(int position, int count) {
AsyncPagedListDiffer.this.mUpdateCallback.onChanged(position, count, (Object)null); }}Copy the code
ListUpdateCallback:
public interface ListUpdateCallback {...void onInserted(int position, int count);
}
Copy the code
AdapterListUpdateCallback:
@Overridepublic void onInserted(int position, int count) {
mAdapter.notifyItemRangeInserted(position, count);
}
Copy the code
The reverse way, look at the text yourself
Reverse source closure:
In the example we started writing using Paging, submitList() is used to set the data, and submiList () calls mDiffer. SubmitList (pagedList) directly:
public void submitList(PagedList<T> pagedList) {
mDiffer.submitList(pagedList);
}
Copy the code
public void submitList(final PagedList<T> pagedList) {
if (mPagedList == null && mSnapshot == null) {
// fast simple first insert
mPagedList = pagedList;
pagedList.addWeakCallback(null, mPagedListCallback);
return; }}Copy the code
AddWeakCallback () is called to add the Callback instance mPagedListCallback
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); }};Copy the code
Source above, mPagedListCallback onInserted () callback mUPdateCallback. OnInserted (), MUPdateCallback here is Differ, created in the PagedListAdapter constructor in AsyncPagedListDiffer directly initialized in the constructor of the AdapterListUpdateCallback object
public AsyncPagedListDiffer(@NonNull RecyclerView.Adapter adapter,
@NonNull DiffUtil.ItemCallback<T> diffCallback) {
mUpdateCallback = new AdapterListUpdateCallback(adapter);
mConfig = new AsyncDifferConfig.Builder<T>(diffCallback).build();
}
Copy the code
So the program execution to the AdapterListUpdateCallback, In AdapterListUpdateCallback. OnInserted () in the direct call to the Adapter notifyItemRangeInserted (position, count) to realize data update, the Adapter is here