Paging Library(分頁加載庫)用于逐步從數據源加載信息,而不會耗費過多的設備資源或者等待太長的時間。
總體概覽
一個常見的需求是獲取很多數據,但是同時也只展示其中的一小部分數據。這就會引起很多問題,比如浪費資源和流量等。
現有的Android APIs可以提供分頁加載的功能,但是也帶來了顯著的限制和缺點:
- CursorAdapter,使得從數據庫加載數據到ListView變得非常容易。但是這是在主線程中查詢數據庫,并且分頁的內容使用低效的 Cursor返回。更多使用CursorAdapter帶來的問題參考Large Database Queries on Android。
- AsyncListUtil提供基于位置的( position-based)分頁加載到 RecyclerView中,但是無法使用不基于位置(non-positional)的分頁加載,而且還強制把null作為占位符。
paging library就是為了解決上述的問題而提出的。
提供的類
DataSource
數據源。根據你想要訪問數據的方式,可以有兩種子類可供選擇:
- KeyedDataSource用于加載從第N到N+1數據。
- TiledDataSource 用于從任意位置的分頁數據。
例如使用 Room persistence library 就可以自動創建返回 TiledDataSource類型的數據:
@Query("select * from users WHERE age > :age order by name DESC, id ASC")
TiledDataSource<User> usersOlderThan(int age);
從DataSource獲取指定數量的數據,并且可以制定預取多少數據。這樣可以最大程度減少加載數據的時間。這個類可以提供更新信息給其他類比如RecyclerView.Adapter來更新 RecyclerView的UI。
這個類是 RecyclerView.Adapter的一個實現類,用于當數據加載完畢時,通知 RecyclerView數據已經到達。 RecyclerView就可以把數據填充進來,取代原來的占位元素。
PagedListAdapter使用后臺線程來計算 PagedList 的改變。
從數據源中產生 LiveData<PagedList>。此外如果使用的是 Room persistence library,DAO還能使用 TiledDataSource生成 LivePagedListProvider。示例代碼:
@Query("SELECT * from users order WHERE age > :age order by name DESC, id ASC")
public abstract LivePagedListProvider<Integer, User> usersOlderThan(int age);
通過上面的類的配合使用,paging library從后臺線程獲取數據流,再在UI線程中展示。例如,當新的item別插入到數據庫, DataSource被更新, LivePagedListProvider在后臺線程產生了新的 PagedList
詳細的流程如下圖:
新生成的 PagedList在主線程中被發送到PagedListAdapter, PagedListAdapter在后臺線程使用 DiffUtil計算新的list和原來的list的差距。當比較完畢, PagedListAdapter調用RecyclerView.Adapter.notifyItemInserted()來通知數據刷新。
下面的示例代碼展示了這些類如何配合工作:
@Dao
interface UserDao {
@Query("SELECT * FROM user ORDER BY lastName ASC")
public abstract LivePagedListProvider<Integer, User> usersByLastName();
}
class MyViewModel extends ViewModel {
public final LiveData<PagedList<User>> usersList;
public MyViewModel(UserDao userDao) {
usersList = userDao.usersByLastName().create(
/* initial load position */ 0,
new PagedList.Config.Builder()
.setPageSize(50)
.setPrefetchDistance(50)
.build());
}
}
class MyActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
RecyclerView recyclerView = findViewById(R.id.user_list);
UserAdapter<User> adapter = new UserAdapter();
viewModel.usersList.observe(this, pagedList -> adapter.setList(pagedList));
recyclerView.setAdapter(adapter);
}
}
class UserAdapter extends PagedListAdapter<User, UserViewHolder> {
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 DiffCallback<User> DIFF_CALLBACK = new DiffCallback<User>() {
@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);
}
}
}
好了,整個Android Architecture Components系列都介紹完了(并沒有,還有一篇WorkManager相關的文章,感興趣的可以看看),感謝各位大佬的閱讀。
整個系列的后兩篇文章相對介紹的比較粗糙一點,主要還是因為時間比較緊張,還有其實Room和Paging Library更像是一個第三方庫,主要還是需要大家去寫一遍感受可能會更清晰一點。
PS.鑒于大家的都建議給一個整體框架的demo,這里可以提供一個更好的方案:Google Android Architecture Components,這是Google官方提供的樣例可以用來參考。
相關文章:
理解Android Architecture Components系列(一)
理解Android Architecture Components系列(二)
理解Android Architecture Components系列之Lifecycle(三)
理解Android Architecture Components系列之LiveData(四)
理解Android Architecture Components系列之ViewModel(五)
理解Android Architecture Components系列之Room(六)
理解Android Architecture Components系列之Paging Library(七)
理解Android Architecture Components系列之WorkManager(八)