paging library(分页库)使用

分页库使您的应用程序能够更轻松地根据需要从数据源加载信息,而不会使设备过载或等待太长的数据库查询时间。

在Model - build.gradle添加依赖

implementation "android.arch.paging:runtime:1.0.0-alpha7"

概观


许多应用程序可以处理大量数据,但只需要随时加载和显示一小部分数据。一个应用程序可能有数千个可能显示的项目,但它可能只需要一次访问几十个项目。如果应用程序不小心,它可能最终会请求它实际上不需要的数据,从而给设备和网络带来性能负担。如果数据与远程数据库存储或同步,则这也会降低应用速度并浪费用户的数据计划。

尽管现有的Android API允许在内容中进行分页,但它们带来了明显的限制和缺陷:

  • CursorAdapter使得将数据库查询结果映射到ListView项目变得更加容易,但它会在UI线程上运行数据库查询,并以低效的方式使用Cursor。有关使用缺点的更多详细信息 CursorAdapter,请参阅博客文章Android上的 大型数据库查询。
  • AsyncListUtil 允许将基于位置的数据分页到a中RecyclerView,但不允许进行非位置分页,并且强制空数据集中的空位占位符。

新的分页库解决了这些问题。该库包含几个类,以便在需要时简化请求数据的过程。这些类还可以与现有的体系结构组件(如Room)无缝 协作。

功能


分页库的一组类允许您完成本节中显示的任务。

定义如何获取数据

使用DataSource该类来定义您需要从中提取分页数据的数据源。根据你需要访问你的数据的方式,你可以扩展它的一个子类:

  • 使用 PageKeyedDataSource 如果页面在加载嵌入一个/上一个键。例如,如果您从网络中提取社交媒体帖子,则可能需要将下一个代码从一个加载传递到后续加载。
  • 使用 ItemKeyedDataSource ,如果你需要使用的数据,从项目ñ获取项目N + 1。例如,如果您正在为讨论应用程序提取线索评论,则可能需要传递一条评论的标识以获取下一条评论的内容。
  • PositionalDataSource 如果您需要从您在数据存储中选择的任何位置获取数据页面,请使用此选项 。此类支持从您选择的任何位置开始请求一组数据项,如“返回从位置1200开始的20个数据项”。

如果您使用Room持久性库来管理数据,它可以生成一个 自动为您DataSource.Factory 生成实例 PositionalDataSource。例如:

@Query("select * from users WHERE age > :age order by name DESC, id ASC")
DataSource.Factory usersOlderThan(int age);

将数据加载到内存中

PagedList从一个类加载数据DataSource。您可以配置一次加载多少数据,以及预取多少数据,从而最大限度地缩短用户等待数据加载的时间。这个类可以向其他类提供更新信号,例如RecyclerView.Adapter,允许您RecyclerView在页面中加载数据时更新您的内容。

根据您的应用程序的体系结构,您有几种使用PagedList该类的选项 。要了解更多信息,请参阅选择数据加载体系结构。

在您的用户界面中呈现数据

PagedListAdapter 班是的实现RecyclerView.Adapter 是从呈现的数据 PagedList。例如,当被装载一个新的页面,该 PagedListAdapter 信号RecyclerView ,该数据已经到达; 这使得RecyclerView 用实际项目替换任何占位符,执行适当的动画。

PagedListAdapter 还采用了后台线程从一个计算变化 PagedList到下(例如,当数据库的变化产生一个新的 PagedList与更新的数据),并调用notifyItem…() 根据需要更新列表中的内容方法。RecyclerView 然后执行必要的更改。例如,如果项目在PagedList版本之间改变位置,则RecyclerView 将该项目移动到列表中的新位置。

观察数据更新

分页库提供以下类来构建 PagedList能够实时更新的容器:

  • LivePagedListBuilder

    这个类 从 你提供的一个生成 。如果使用 Room持久性库来管理数据库,则DAO可以 使用以下示例中的示例为您 生成 :LiveData<PagedList>DataSource.FactoryDataSource.FactoryPositionalDataSource

    LiveData> pagedItems = LivePagedListBuilder(myDataSource, /* page size */ 50)
            .setFetchExecutor(myNetworkExecutor)
            .build();
    
  • RxPagedListBuilder

    这个类提供了 类似于RxJava2的功能LivePagedListBuilder。该类由图书馆基于RxJava2的工件 android.arch.paging提供:rxjava2:1.0.0-alpha1。使用这个类,你可以在你的实现中构造FlowableObservable实现对象 PagedList,如下面的代码片段所示:

    Flowable> pagedItems = RxPagedListBuilder(myDataSource, /* page size */ 50)
            .setFetchScheduler(myNetworkScheduler)
            .buildFlowable(BackpressureStrategy.LATEST);
    

创建一个数据流

Paging Library的组件一起组织来自后台线程生成器的数据流,并在UI线程上呈现。例如,当一个新的项被插入在数据库中, DataSource被无效,和 或 产生新的在后台线程。LiveData PagedList FlowablePagedListPagedList

paging library(分页库)使用_第1张图片
分页库组件在后台线程中完成大部分工作,所以它们不会给UI线程造成负担

新创建的内容PagedList 被发送到 PagedListAdapter UI线程上。该 PagedListAdapter 然后使用DiffUtil一个后台线程来计算当前列表和新列表之间的差异。比较结束后, PagedListAdapter 使用列表差异信息进行适当的调用以RecyclerView.Adapter.notifyItemInserted() 表示新项目已插入。

RecyclerViewUI线程就知道它只有绑定一个新的项目,它的动画出现在屏幕上。

数据库示例


以下代码片段显示了将所有片段一起使用的几种可能方式。

使用LiveData观察分页数据

下面的代码片段显示了所有一起工作的部分。随着用户在数据库中的添加,删除或更改,其RecyclerView内容会自动而有效地更新:

@Dao
interface UserDao {
    // The Integer type parameter tells Room to use a PositionalDataSource    // object, with position-based loading under the hood.    @Query("SELECT * FROM user ORDER BY lastName ASC")   
    public abstract DataSource.Factory usersByLastName();
}

class MyViewModel extends ViewModel {
    public final LiveData> usersList;

    public MyViewModel(UserDao userDao) {
        usersList = new LivePagedListBuilder<>(
                userDao.usersByLastName(), /* page size */ 20).build();
    }
}

class MyActivity extends AppCompatActivity {
    private UserAdapter mAdapter;

    @Override
    public void onCreate(Bundle savedState) {
        super.onCreate(savedState);
        MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);       
        RecyclerView recyclerView = findViewById(R.id.user_list);
        mAdapter = new UserAdapter();
        viewModel.usersList.observe(this, pagedList ->
                mAdapter.submitList(pagedList));
        recyclerView.setAdapter(mAdapter);
    }
}

class UserAdapter extends PagedListAdapter {

    public UserAdapter() {
        super(DIFF_CALLBACK);
    }

    @Override
    public void onBindViewHolder(UserViewHolder holder, int position) {
        User user = getItem(position);
        if (user != null) {
            holder.bindTo(user);
        } else {
            // Null defines a placeholder item - PagedListAdapter will automatically invalidate           
            // this row when the actual object is loaded from the database           
            holder.clear();
        }
    }

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

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

使用RxJava2观察分页数据

如果您更喜欢使用 RxJava2而不是 LiveData,则可以创建一个Observable或一个Flowable对象:

class MyViewModel extends ViewModel {

    public final Flowable> usersList;

    public MyViewModel(UserDao userDao) {
        usersList = new RxPagedListBuilder<>(userDao.usersByLastName(),
                /* page size */ 50).buildFlowable(BackpressureStrategy.LATEST);
    }
}

然后,您可以使用以下代码片段中的代码开始和停止观察数据:

class MyActivity extends AppCompatActivity {
    private UserAdapter mAdapter;
    private final CompositeDisposable mDisposable = new CompositeDisposable();

    @Override
    public void onCreate(Bundle savedState) {
        super.onCreate(savedState);
        MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
        RecyclerView recyclerView = findViewById(R.id.user_list);
        mAdapter = new UserAdapter();
        recyclerView.setAdapter(mAdapter);
    }

    @Override
    protected void onStart() {
        super.onStart();
        myDisposable.add(mViewModel.usersList.subscribe(flowableList ->
                mAdapter.submitList(flowableList)));
    }

    @Override
    protected void onStop() {
        super.onStop();
        mDisposable.clear();
    }
}

基于RxJava2的解决方案的代码UserDao与基于解决方案 的代码UserAdapter相同 。LiveData

选择一个数据加载架构


寻呼数据库有两种主要的方法来寻呼数据:

网络或数据库

首先,您可以从单一来源进行分页 - 本地存储或网络。在这种情况下,使用a 将已加载的数据馈送到UI中,如上面的示例中所示。LiveData<PagedList>

要指定您的数据来源,请传递DataSource.FactoryLivePagedListBuilder

paging library(分页库)使用_第2张图片
单一数据源提供DataSource.Factory来加载内容

当观察数据库时,数据库在发生内容更改时将“推送”新的PagedList。在网络分页情况下(当后端不发送更新时),像刷卡到刷新这样的信号可以通过使当前数据源无效来“拉”新的分页列表。这将异步刷新所有数据。

PagingWithNetworkSample中的内存+网络存储库实现 显示了如何在处理刷新刷新,网络错误和重试时DataSource.Factory 使用Retrofit来实现网络 。

网络数据库

在第二种情况下,您可以从本地存储页面进行寻址,本地存储页面本身会从网络中寻找其他数据。这通常是为了最大限度地减少网络负载并提供更好的低连接体验 - 数据库被用作存储在后端数据的缓存。

在这种情况下,用 从数据库页面的内容,并传递 到 观察出的数据,信号。LiveData<PagedList>BoundaryCallbackLivePagedListBuilder

paging library(分页库)使用_第3张图片
数据库是网络数据的缓存 - UI从数据库加载数据,并在数据不足时从网络加载到数据库时发送信号

然后将这些回调连接到网络请求,这些请求将直接将数据存储在数据库中。用户界面订阅了数据库更新,所以新的内容自动流向任何观察用户界面。

PagingWithNetworkSample 中的数据库+网络存储库 显示了如何BoundaryCallback 使用Retrofit实施网络,同时处理刷卡刷新,网络错误和重试。

你可能感兴趣的