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

  • 相关阅读:
    204. Count Primes (Integer)
    203. Remove Linked List Elements (List)
    202. Happy Number (INT)
    201. Bitwise AND of Numbers Range (Bit)
    200. Number of Islands (Graph)
    199. Binary Tree Right Side View (Tree, Stack)
    198. House Robber(Array; DP)
    191. Number of 1 Bits (Int; Bit)
    190. Reverse Bits (Int; Bit)
    189. Rotate Array(Array)
  • 原文地址:https://www.cnblogs.com/milovetingting/p/12726872.html
Copyright © 2011-2022 走看看