• Jetpack学习-Paging


    个人博客

    http://www.milovetingting.cn

    Jetpack学习-Paging

    Paging是什么

    分页库可一次加载和显示一小块数据。按需载入部分数据会减少网络带宽和系统资源的使用量。

    简单使用

    引入Paging

    在需要引入Paging模块的build.gradle中配置

        def paging_version = "2.1.0"
        implementation "androidx.paging:paging-runtime:$paging_version"
    

    定义Bean

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

    需要重写equalshashCode方法,后面比较数据时会用到

    定义DataSource

    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

    public class StudentDataSourceFactory extends DataSource.Factory<Integer, Student> {
        @NonNull
        @Override
        public DataSource<Integer, Student> create() {
            StudentDataSource dataSource = new StudentDataSource();
            return dataSource;
        }
    }
    

    定义ViewModel

    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

    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) {
            //LayoutInflater.from(parent.getContext()).inflate(R.layout.item_paging, null);不能在宽度上满屏
            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);
            }
        }
    
    }
    

    显示数据

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

    public LiveData<PagedList<Student>> getListLiveData() {
            return listLiveData;
        }
    

    listLiveData在构造方法中赋值

    public StudentViewModel() {
            StudentDataSourceFactory factory = new StudentDataSourceFactory();
            this.listLiveData = new LivePagedListBuilder<Integer, Student>(factory, Config.SIZE).build();
        }
    

    通过LivePagedListBuilder的build方法赋值

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

    调用create

    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对象

    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,当LiveDataonActive方法回调时,就会执行mRefreshRunnable

    RefreshRunnablerun方法,先执行compute然后会通过ComputableLiveData.this.mLiveData.postValue(value)刷新

    先来看compute方法

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

    这个实现在我们定义的类中

    public DataSource<Integer, Student> create() {
            StudentDataSource dataSource = new StudentDataSource();
            return dataSource;
        }
    

    然后通过PagedList.Builder.build()对mList进行赋值

    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.create方法

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

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

    调用dispatchLoadInitial

    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;
    
                // Note enablePlaceholders will be false here, but we don't have a way to communicate
                // this to PositionalDataSource. This is fine, because only the list and its position
                // offset will be consumed by the LoadInitialCallback.
                mSource.dispatchLoadInitial(false, convertPosition, initialLoadSize,
                        pageSize, mainThreadExecutor, receiver);
            }
    

    调用dispatchLoadInitial

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

    调用loadInitial,这里调用到了我们定义的StudentDataSource

    public void loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialCallback<Student> callback) {
            callback.onResult(getStudents(0, Config.SIZE), 0, 1000);
        }
    

    调用onResult

    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

    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中的回调就会执行

    public void onChanged(PagedList<Student> students) {
                    adapter.submitList(students);
                }
    

    会调用submitList

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

    调用submitList

    public void submitList(@Nullable PagedList<T> pagedList) {
            this.submitList(pagedList, (Runnable)null);
        }
    

    调用submitList

    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的原理先分析到这里。什么?这也太简单了吧,根本没有讲清楚具体的流程啊!你是不是不会原理啊!!!嗯~~~说对了,目前还只是理解到这里,后续再补充(手动尴尬)!

  • 相关阅读:
    30-语言入门-30-分数加减法
    29-语言入门-29-两点距离
    bootstrapcss3触屏滑块轮播图
    input输入样式,动画
    HTML5夜空烟花绽放动画效果
    精美留言、评论框,带微博表情
    Sublime Text 3汉化中文版
    直播英国脱欧各国反应?谁将是最大赢家?
    品牌关键字的重要性?是什么呢
    网站收录之网络推广
  • 原文地址:https://www.cnblogs.com/milovetingting/p/12726872.html
Copyright © 2020-2023  润新知