Android Jetpack最近推出分页库:Paging Library,它结合RecyclerView来加载大量数据与分页显示。这意味着我们不用再手动去分页请求,也不用担心加载大量数据导致而页面ANR。分页库可以帮助我们应用程序观察和每次加载合理的数据,这样带来的优势包括:数据请求占用更少带宽、更少系统资源;在数据更新或者刷新的同时,应用程序可以快速响应用户交互。
分页库的关键组件是PagedList,它是异步加载应用程序数据块或者页面的集合。PagedList与PagedListAdapter相结合,把数据加载到RecyclerView里。这几个类协同工作,请求与显示加载到的内容。导入依赖库:
implementation 'android.arch.paging:runtime:1.0.0'
分页库实现观察者模式接口。库的核心组件会创建LiveData
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 loadingState =
// boundaryCallback.getLoadingState();
}
}
public class ConcertBoundaryCallback
extends PagedList.BoundaryCallback {
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 concertsByDate();
}
public class ConcertViewModel extends ViewModel {
private ConcertDao mConcertDao;
public final LiveData> 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 {
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 DIFF_CALLBACK =
new DiffUtil.ItemCallback() {
// 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> 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去显示。