Android Jetpack的分页库

Android Jetpack最近推出分页库:Paging Library,它结合RecyclerView来加载大量数据与分页显示。这意味着我们不用再手动去分页请求,也不用担心加载大量数据导致而页面ANR。分页库可以帮助我们应用程序观察和每次加载合理的数据,这样带来的优势包括:数据请求占用更少带宽、更少系统资源;在数据更新或者刷新的同时,应用程序可以快速响应用户交互。

分页库的关键组件是PagedList,它是异步加载应用程序数据块或者页面的集合。PagedList与PagedListAdapter相结合,把数据加载到RecyclerView里。这几个类协同工作,请求与显示加载到的内容。导入依赖库:

implementation 'android.arch.paging:runtime:1.0.0'

分页库实现观察者模式接口。库的核心组件会创建LiveData<PagedList>,用于UI交互的观察。我们的应用程序UI可以显示从PagedList对象生成的内容,同时需要遵循UI Controller的lifecycle的生命周期。它支持不同的数据结构:

1、只有网络

为了显示来自后台服务器数据,使用Retrofit API的同步版本来加载数据到自定义的DataSource对象中。

2、只有数据库

创建RecyclerView来观察本地存储,可以使用 Room persistence library作为数据库。这样,无论是向数据库插入还是更改,这些变化会自动同步到RecyclerView来显示。

3、同时存在网络和数据库

在观察数据库后,我们可以使用PagedList.BoundaryCallback来监听数据库是否有数据。如果没有,我们可以通过网络请求,把数据插到数据库。边界回调示例:

public class ConcertViewModel {
    public ConcertSearchResult search(String query) {
        ConcertBoundaryCallback boundaryCallback =
                new ConcertBoundaryCallback(query, myService, myCache);
        // Use a LiveData object to communicate your network's state back
        // to your app's UI, as in the following example. Note that error
        // handling isn't shown in this snippet.
        // LiveData<NetworkState> loadingState =
        //      boundaryCallback.getLoadingState();
    }
}

public class ConcertBoundaryCallback
        extends PagedList.BoundaryCallback<Concert> {
    private String mQuery;
    private MyService mService;
    private MyLocalCache mCache;

    public ConcertBoundaryCallback(String query, MyService service,
            MyLocalCache cache) {
        mQuery = query;
        // ...
    }

    // Requests initial data from the network, replacing all content currently
    // in the database.
    @Override
    public void onZeroItemsLoaded() {
        requestAndReplaceInitialData(mQuery);
    }

    // Requests additional data from the network, appending the results to the
    // end of the database's existing data.
    @Override
    public void onItemAtEndLoaded(@NonNull Concert itemAtEnd) {
        requestAndAppendData(mQuery, itemAtEnd.key);
    }
}

网络请求时,我们需要监听网络状态以及处理网络异常。我们应该判断每个请求是否会失败,在网络不可用时尽可能优雅地恢复请求。比如,当无法请求到数据时,我们应该提供重请求button,让用户来选择是否要进行二次请求。如果是数据分页阶段发生错误,最好是能够自动重请求。

对于数据库情景的分页显示,我们可以使用LiveData来观察数据。包括数据库的增加、删除、修改、查询的具体操作,都可以自动同步到RecyclerView去显示,可以这样写:

@Dao
public interface ConcertDao {
    @Query("SELECT * FROM concerts ORDER BY date DESC")
    DataSource.Factory<Integer, Concert> concertsByDate();
}

public class ConcertViewModel extends ViewModel {
    private ConcertDao mConcertDao;
    public final LiveData<PagedList<Concert>> concertList;

    public ConcertViewModel(ConcertDao concertDao) {
        mConcertDao = concertDao;
        concertList = new LivePagedListBuilder<>(
            mConcertDao.concertsByDate(), /* page size */ 20).build();
    }
}

public class ConcertActivity extends AppCompatActivity {
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ConcertViewModel viewModel =
                ViewModelProviders.of(this).get(ConcertViewModel.class);
        RecyclerView recyclerView = findViewById(R.id.concert_list);
        ConcertAdapter adapter = new ConcertAdapter();
        viewModel.concertList.observe(this, adapter::submitList);
        recyclerView.setAdapter(adapter);
    }
}

public class ConcertAdapter
        extends PagedListAdapter<Concert, ConcertViewHolder> {
    protected ConcertAdapter() {
        super(DIFF_CALLBACK);
    }

    @Override
    public void onBindViewHolder(@NonNull ConcertViewHolder holder,
            int position) {
        Concert concert = getItem(position);
        if (concert != null) {
            holder.bindTo(concert);
        } else {
            holder.clear();
        }
    }

    private static DiffUtil.ItemCallback<Concert> DIFF_CALLBACK =
            new DiffUtil.ItemCallback<Concert>() {
        // Concert details may have changed if reloaded from the database,
        // but ID is fixed.
        @Override
        public boolean areItemsTheSame(Concert oldConcert, Concert newConcert) {
            return oldConcert.getId() == newConcert.getId();
        }

        @Override
        public boolean areContentsTheSame(Concert oldConcert,
                Concert newConcert) {
            return oldConcert.equals(newConcert);
        }
    };
}

除了LiveData,我们也可以使用RxJava2,需要创建Observable或者Flowable对象:

public class ConcertViewModel extends ViewModel {
    private ConcertDao mConcertDao;
    public final Flowable<PagedList<Concert>> concertList;

    public ConcertViewModel(ConcertDao concertDao) {
        mConcertDao = concertDao;

        concertList = new RxPagedListBuilder<>(
                mConcertDao.concertsByDate(), /* page size */ 50)
                        .buildFlowable(BackpressureStrategy.LATEST);
    }
}
You can then start and stop observing the data using the code in the following snippet:

KOTLINJAVA
public class ConcertActivity extends AppCompatActivity {
    private ConcertAdapter mAdapter;
    private ConcertViewModel mViewModel;

    private CompositeDisposable mDisposable = new CompositeDisposable();

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        RecyclerView recyclerView = findViewById(R.id.concert_list);

        mViewModel = ViewModelProviders.of(this).get(ConcertViewModel.class);
        mAdapter = new ConcertAdapter();
        recyclerView.setAdapter(mAdapter);
    }

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

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

Jetpack的分页库,为我们开发者解决手动分页请求的问题,而且可以通过LiveData或者RxJava2,自动把数据同步到RecyclerView去显示。

发布了63 篇原创文章 · 获赞 179 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/u011686167/article/details/85498788