(七、1)分頁庫概述

分頁庫使您可以更輕松地在應(yīng)用程序的RecyclerView中逐步和優(yōu)雅地加載數(shù)據(jù)。
許多應(yīng)用程序使用包含大量項目的數(shù)據(jù)源中的數(shù)據(jù)屁奏,但一次只顯示一小部分。
分頁庫可幫助您的應(yīng)用觀察并顯示此數(shù)據(jù)的合理子集错负。 此功能有幾個優(yōu)點:

  • 數(shù)據(jù)請求消耗的網(wǎng)絡(luò)帶寬更少坟瓢,系統(tǒng)資源更少。 擁有計量或小數(shù)據(jù)計劃的用戶會欣賞這些具有數(shù)據(jù)意識的應(yīng)用程序犹撒。
  • 即使在數(shù)據(jù)更新和刷新期間折联,應(yīng)用程序仍會繼續(xù)快速響應(yīng)用戶輸入。

如果您的應(yīng)用已包含分頁數(shù)據(jù)和顯示列表的邏輯油航,我們就如何更新現(xiàn)有應(yīng)用提供了指導(dǎo)崭庸。
本指南概述了如何使用分頁庫來請求和顯示用戶希望在更經(jīng)濟地消耗系統(tǒng)資源時看到的數(shù)據(jù)怀浆。 有關(guān)特定于應(yīng)用程序體系結(jié)構(gòu)層的指南谊囚,請參閱以下相關(guān)頁面:

  • UI組件和注意事項
  • 數(shù)據(jù)組件和注意事項

注意:無論您是僅使用設(shè)備內(nèi)部數(shù)據(jù)庫還是從應(yīng)用程序的后端獲取信息,分頁庫都可以幫助您順利地在UI列表容器中顯示數(shù)據(jù)执赡。

一镰踏、庫架構(gòu)

Paging Library的關(guān)鍵組件是PagedList類,它是一個異步加載應(yīng)用程序數(shù)據(jù)塊或頁面的集合沙合。 此類充當(dāng)應(yīng)用程序架構(gòu)的其他部分之間的中介:

Data

PagedList的每個實例都從其DataSource加載應(yīng)用程序數(shù)據(jù)的最新快照奠伪。 數(shù)據(jù)從應(yīng)用程序的后端或數(shù)據(jù)庫流入PagedList對象。
分頁庫支持各種應(yīng)用程序體系結(jié)構(gòu)首懈,包括獨立數(shù)據(jù)庫和與后端服務(wù)器通信的數(shù)據(jù)庫绊率。

UI

PagedList類與PagedListAdapter一起使用,可以將項目加載到RecyclerView中究履。 這些類一起工作以在加載內(nèi)容時獲取和顯示內(nèi)容滤否,預(yù)取視圖外內(nèi)容并動畫內(nèi)容更改。

分頁庫實現(xiàn)了應(yīng)用程序體系結(jié)構(gòu)指南中建議的觀察者模式最仑。 特別是藐俺,庫的核心組件創(chuàng)建了一個UI可以觀察的LiveData <PagedList>(或等效的基于RxJava2的類)的實例炊甲。 然后,您的應(yīng)用程序的UI可以在生成PagedList對象時顯示內(nèi)容欲芹,同時尊重UI控制器的生命周期卿啡。

二、支持不同的數(shù)據(jù)架構(gòu)

分頁庫支持應(yīng)用程序體系結(jié)構(gòu)菱父,包括應(yīng)用程序僅從后端服務(wù)器獲取數(shù)據(jù)颈娜,僅從設(shè)備上數(shù)據(jù)庫或兩個源的組合獲取數(shù)據(jù)的應(yīng)用程序體系結(jié)構(gòu)。 本節(jié)為每種情況提供建議浙宜。

我們提供了用于不同數(shù)據(jù)架構(gòu)的推薦模式的示例揭鳞。 要查看它們,請參閱GitHub上的PagingWithNetwork示例梆奈。

僅限網(wǎng)絡(luò)

要顯示來自后端服務(wù)器的數(shù)據(jù)野崇,請使用Retrofit API的同步版本將信息加載到您自己的自定義DataSource對象中。

僅數(shù)據(jù)庫

設(shè)置RecyclerView以觀察本地存儲亩钟,最好使用Room persistence library乓梨。 這樣,無論何時在應(yīng)用程序的數(shù)據(jù)庫中插入或修改數(shù)據(jù)清酥,這些更改都會自動反映在顯示此數(shù)據(jù)的RecyclerView中扶镀。

網(wǎng)絡(luò)和數(shù)據(jù)庫

在開始觀察數(shù)據(jù)庫之后,可以使用PagedList.BoundaryCallback監(jiān)聽數(shù)據(jù)庫何時沒有數(shù)據(jù)焰轻。 然后臭觉,您可以從網(wǎng)絡(luò)中獲取更多項目并將其插入數(shù)據(jù)庫。 如果您的UI正在觀察數(shù)據(jù)庫辱志,那就是您需要做的蝠筑。
以下代碼段顯示了邊界回調(diào)的示例用法:

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);
    }
}

三、處理網(wǎng)絡(luò)錯誤

當(dāng)使用網(wǎng)絡(luò)獲取或分頁您正在使用分頁庫顯示的數(shù)據(jù)時揩懒,重要的是不要將網(wǎng)絡(luò)視為“可用”或“不可用”什乙,因為許多連接是間歇性的或片狀的:

  • 特定服務(wù)器可能無法響應(yīng)網(wǎng)絡(luò)請求。
  • 設(shè)備可能連接到緩慢或弱的網(wǎng)絡(luò)已球。

相反臣镣,您的應(yīng)用應(yīng)檢查每個失敗請求,并在網(wǎng)絡(luò)不可用的情況下盡可能優(yōu)雅地恢復(fù)智亮。 例如忆某,您可以提供“重試”按鈕,供用戶選擇數(shù)據(jù)刷新步驟是否不起作用阔蛉。 如果在數(shù)據(jù)分頁步驟期間發(fā)生錯誤弃舒,則最好自動重試分頁請求。

四馍忽、更新現(xiàn)有應(yīng)用

如果您的應(yīng)用已經(jīng)消耗了數(shù)據(jù)庫或后端源中的數(shù)據(jù)棒坏,則可以直接升級到分頁庫提供的功能燕差。 本節(jié)介紹如何升級具有通用現(xiàn)有設(shè)計的應(yīng)用程序。

定制分頁解決方案

如果使用自定義功能從應(yīng)用程序的數(shù)據(jù)源加載小的數(shù)據(jù)子集坝冕,則可以將此邏輯替換為PagedList類中的邏輯徒探。 PagedList的實例提供與公共數(shù)據(jù)源的內(nèi)置連接。 這些實例還為您可能包含在應(yīng)用程序UI中的RecyclerView對象提供適配器喂窟。

使用列表而不是頁面加載數(shù)據(jù)

如果使用內(nèi)存列表作為UI適配器的后備數(shù)據(jù)結(jié)構(gòu)测暗,請考慮如果列表中的項目數(shù)可能變大,則使用PagedList類觀察數(shù)據(jù)更新磨澡。 PagedList的實例可以使用LiveData <PagedList>或Observable <List>將數(shù)據(jù)更新傳遞到應(yīng)用程序的UI碗啄,從而最大限度地減少加載時間和內(nèi)存使用量。 更好的是稳摄,在應(yīng)用程序中用PagedList對象替換List對象不需要對應(yīng)用程序的UI結(jié)構(gòu)或數(shù)據(jù)更新邏輯進行任何更改稚字。

使用CursorAdapter將數(shù)據(jù)光標(biāo)與列表視圖相關(guān)聯(lián)

您的應(yīng)用程序可能使用CursorAdapter將Cursor中的數(shù)據(jù)與ListView相關(guān)聯(lián)。 在這種情況下厦酬,您通常需要從ListView遷移到RecyclerView作為應(yīng)用程序的列表UI容器胆描,然后將Cursor組件替換為Room或PositionalDataSource,具體取決于Cursor實例是否訪問SQLite數(shù)據(jù)庫仗阅。

在某些情況下昌讲,例如在處理Spinner實例時,您只提供適配器本身减噪。 然后短绸,庫將獲取加載到該適配器中的數(shù)據(jù)并為您顯示數(shù)據(jù)。 在這些情況下筹裕,將適配器數(shù)據(jù)的類型更改為LiveData <PagedList>醋闭,然后在嘗試讓庫類在UI中對這些項進行膨脹之前,將此列表包裝在ArrayAdapter對象中饶碘。

使用AsyncListUtil異步加載內(nèi)容

如果您使用AsyncListUtil對象異步加載和顯示信息組目尖,則分頁庫可讓您更輕松地加載數(shù)據(jù):

  • 您的數(shù)據(jù)不需要是位置的。 分頁庫允許您使用網(wǎng)絡(luò)提供的密鑰直接從后端加載數(shù)據(jù)扎运。
  • 您的數(shù)據(jù)可能非常大。 使用分頁庫饮戳,您可以將數(shù)據(jù)加載到頁面中豪治,直到?jīng)]有剩余數(shù)據(jù)。
  • 您可以更輕松地觀察數(shù)據(jù)扯罐。 Paging庫可以顯示應(yīng)用程序的ViewModel在可觀察數(shù)據(jù)結(jié)構(gòu)中保存的數(shù)據(jù)负拟。

五、數(shù)據(jù)庫示例

以下代碼片段顯示了將所有部分協(xié)同工作的幾種可能方法歹河。

使用LiveData觀察分頁數(shù)據(jù)

以下代碼段顯示了所有協(xié)同工作掩浙。 隨著在數(shù)據(jù)庫中添加花吟,刪除或更改音樂會事件,RecyclerView中的內(nèi)容將自動且有效地更新:

@Dao
public interface ConcertDao {
    // The Integer type parameter tells Room to use a PositionalDataSource
    // object, with position-based loading under the hood.
    @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 {
            // Null defines a placeholder item - PagedListAdapter automatically
            // invalidates this row when the actual object is loaded from the
            // database.
            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);
        }
    };
}
使用RxJava2觀察分頁數(shù)據(jù)

如果您更喜歡使用RxJava2而不是LiveData厨姚,則可以創(chuàng)建一個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);
    }
}

然后衅澈,您可以使用以下代碼段中的代碼開始和停止觀察數(shù)據(jù):

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();
    }
}

對于基于RxJava2的解決方案,ConcertDao和ConcertAdapter的代碼與基于LiveData的解決方案的代碼相同谬墙。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末今布,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子拭抬,更是在濱河造成了極大的恐慌部默,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件造虎,死亡現(xiàn)場離奇詭異傅蹂,居然都是意外死亡,警方通過查閱死者的電腦和手機算凿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門贬派,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人澎媒,你說我怎么就攤上這事搞乏。” “怎么了戒努?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵请敦,是天一觀的道長。 經(jīng)常有香客問我储玫,道長侍筛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任撒穷,我火速辦了婚禮匣椰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘端礼。我一直安慰自己禽笑,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布蛤奥。 她就那樣靜靜地躺著佳镜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪凡桥。 梳的紋絲不亂的頭發(fā)上蟀伸,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機與錄音,去河邊找鬼啊掏。 笑死蠢络,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的迟蜜。 我是一名探鬼主播刹孔,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼小泉!你這毒婦竟也來了芦疏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤微姊,失蹤者是張志新(化名)和其女友劉穎酸茴,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體兢交,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡薪捍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了配喳。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片酪穿。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖晴裹,靈堂內(nèi)的尸體忽然破棺而出被济,到底是詐尸還是另有隱情,我是刑警寧澤涧团,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布只磷,位于F島的核電站,受9級特大地震影響泌绣,放射性物質(zhì)發(fā)生泄漏钮追。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一阿迈、第九天 我趴在偏房一處隱蔽的房頂上張望元媚。 院中可真熱鬧,春花似錦苗沧、人聲如沸刊棕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鞠绰。三九已至,卻和暖如春飒焦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工牺荠, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留翁巍,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓休雌,卻偏偏與公主長得像灶壶,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子杈曲,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,509評論 25 707
  • 用兩張圖告訴你驰凛,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 12,693評論 2 59
  • 今天是讀書日。 作為一名讀書活動的組織者涌献,我不可能無視地飄過胚宦。 我自然地想到一個審視:我們?yōu)槭裁醋x書? 每個人的想...
    于斯的日記閱讀 577評論 1 4
  • 日志復(fù)制機制:主動-主動模式 相同的操作在master-slaver上執(zhí)行;主動-被動模式 在主動上執(zhí)行燕垃,將狀態(tài)復(fù)...
    areece閱讀 137評論 0 0
  • 初中一年級的時候枢劝,要到鎮(zhèn)上上初中,要路過鎮(zhèn)上二家飯館卜壕,但從來沒有進去過您旁,只是看過許多人在門口的桌上上吃著金黃的油條...
    老船長YY閱讀 210評論 0 0