[譯]后臺數(shù)據(jù)加載 - Android TV應(yīng)用手冊教程十五

版權(quán)聲明:本文為博主原創(chuàng)翻譯文章,轉(zhuǎn)載請注明出處。

推薦:
歡迎關(guān)注我創(chuàng)建的Android TV 簡書專題,會定期給大家分享一些AndroidTv相關(guān)的內(nèi)容:
http://www.lxweimin.com/c/3f0ab61a1322


LoaderManager

使用Loader類加載后臺數(shù)據(jù)

本教程正在解釋Leanback支持庫,通??常用于顯示內(nèi)容信息列表。 所以開發(fā)人員可能想要加載許多元數(shù)據(jù)來顯示內(nèi)容。 當(dāng)Activity或Fragment需要準(zhǔn)備大量的數(shù)據(jù)時(shí),最好在后臺加載數(shù)據(jù)。

為此,我們可以使用Loader和LoaderManager 。 它們有助于實(shí)現(xiàn)后臺數(shù)據(jù)加載,然后根據(jù)這些加載的數(shù)據(jù)更新UI。 我將在本章中實(shí)現(xiàn)這個(gè)Loader 。 要了解Loader ,我發(fā)現(xiàn)了一個(gè)非常好的文章,詳細(xì)了解Loader和LoaderManager (從第1部分到第4部分)。 這篇文章是一個(gè)基礎(chǔ)課程,所以請參考上述文章進(jìn)一步了解。

作為文章的總結(jié),使用LoaderManager和Loader優(yōu)點(diǎn)是

  • 數(shù)據(jù)加載過程可以獨(dú)立于活動(dòng)生命周期。

    • LoaderManager是Activity唯一實(shí)例(singleton),它可以處理Loader的生命周期,如啟動(dòng),停止,重置。 通過使用Loader ,加載過程可以獨(dú)立于Activity生命周期。 最有用的情況之一是當(dāng)顯示旋轉(zhuǎn)發(fā)生配置更改(這是Android手機(jī)而不是Android TV)時(shí), Activity將被銷毀,但是Loader的信息保留。
  • UI線程,后臺線程處理很容易。

    • 這與AsyncTask非常相似,因此我們可以異步地在后臺線程中準(zhǔn)備數(shù)據(jù),并獲得回調(diào)以在UI線程上更新UI。 它使我們能夠輕松開發(fā)“優(yōu)秀”軟件架構(gòu),從而減少UI線程的任務(wù)。
      (另請參見AsyncTask使用情況匯總 。
  • Loader是事件驅(qū)動(dòng)的,數(shù)據(jù)處理簡單方便。

    • 例如,當(dāng)數(shù)據(jù)源發(fā)生變化時(shí),我們可以得到回調(diào)。 我們可以根據(jù)數(shù)據(jù)更改輕松更新UI。

要了解實(shí)現(xiàn),我們需要注意Activity / Fragment side和Loader之間的關(guān)系。

Activity,Loader和LoaderManager之間的關(guān)系

LoaderManager

LoaderManager是Activity唯一實(shí)例,并且是 NOT系統(tǒng)/應(yīng)用程序唯一實(shí)例。 因此,不可能使用從Activity B的Activity A Loader A中準(zhǔn)備的數(shù)據(jù)。

舉例:最低實(shí)施

我們來看看在MainFragment類中實(shí)現(xiàn)Loader的MainFragment 。 我們需要使用LoaderManager.LoaderCallbacks來處理如何在UI上顯示數(shù)據(jù)的兩個(gè)模塊, Loader方面負(fù)責(zé)后臺數(shù)據(jù)加載過程和Activity方面使用LoaderManager。LoaderCallbacks在UI上顯示數(shù)據(jù)。

Activity/Fragment方面

1.我們將通過getLoaderManger().initLoader(id, args, callback)注冊加載器,其中

  • id :加載程序的id 這在每個(gè)Activity中都應(yīng)該是相同的,可以在不同的Activity中使用相同的id號,因?yàn)槊總€(gè)Activity/Fragment都存在LoaderManager實(shí)例。

2.實(shí)現(xiàn)LoaderManager.LoaderCallbacks ,我們需要重寫3種方法。

  • onCreateLoader - 我們將在這里實(shí)例化Loader 。
  • onLoadFinished - 當(dāng)Loader完成準(zhǔn)備數(shù)據(jù)時(shí),將調(diào)用此數(shù)據(jù),數(shù)據(jù)將顯示在此方法中的UI上。
  • onLoaderReset - 如果數(shù)據(jù)加載失敗或停止,將調(diào)用此方法。

首先,我們在onActivityCreated中onActivityCreated并注冊LoaderManager.LoaderCallbacks 。 然后執(zhí)行回調(diào)的模擬如下。

    private static final int VIDEO_ITEM_LOADER_ID = 1;

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        getLoaderManager().initLoader(VIDEO_ITEM_LOADER_ID, null, new MainFragmentLoaderCallbacks());

        ...
    }


    private class MainFragmentLoaderCallbacks implements LoaderManager.LoaderCallbacks<HashMap<String, List<Movie>>> {
        @Override
        public Loader<HashMap<String, List<Movie>>> onCreateLoader(int id, Bundle args) {
            /* Create new Loader */
            Log.d(TAG, "VideoItemLoader: onCreateLoader");
            return new VideoItemLoader();
        }

        @Override
        public void onLoadFinished(Loader<HashMap<String, List<Movie>>> loader, HashMap<String, List<Movie>> data) {
            Log.d(TAG, "VideoItemLoader: onLoadFinished");
            /* Loader data has prepared. Start updating UI here */
        }

        @Override
        public void onLoaderReset(Loader<HashMap<String, List<Movie>>> loader) {
            Log.d(TAG, "VideoItemLoader: onLoadReset");
            /* When it is called, Loader data is now unavailable due to some reason. */

        }
    }

onCreateLoader方法的返回值將是Loader的實(shí)例。 現(xiàn)在我們要實(shí)現(xiàn)Loader方面。

Loader

Loader被指定為onCreateLoader的返回值。 我在data包中創(chuàng)建了一個(gè)新類VideoItemLoader ,并擴(kuò)展AsyanTaskLoader<D>開始。

需要執(zhí)行至少2種方法(覆蓋)。

  • Constructor - 父類, AsyncTaskLoader ,需要上下文。 在構(gòu)造函數(shù)中傳遞它。
  • loadInBackground() - 這就像doInBackground()方法。

你“必須”實(shí)現(xiàn)的是兩種方法。 但是當(dāng)我嘗試它時(shí), loadInBackground()從來沒有被調(diào)用過這個(gè)實(shí)現(xiàn)。 我需要在其中執(zhí)行forceLoad()方法

  • onStartLoading()

顯式啟動(dòng)加載。 下面是這三種方法的代碼實(shí)現(xiàn)。

VideoItemLoader.java

/**
 * Loader class which prepares Movie class data
 */
public class VideoItemLoader extends AsyncTaskLoader<LinkedHashMap<String, List<Movie>>> {

    private static final String TAG = VideoItemLoader.class.getSimpleName();

    public VideoItemLoader(Context context) {
        super(context);
    }

    @Override
    public LinkedHashMap<String, List<Movie>> loadInBackground() {
        Log.d(TAG, "loadInBackground");

        /*
         * Executed in background thread.
         * Prepare data here, it may take long time (Database access, URL connection, etc).
         * return value is used in onLoadFinished() method in Activity/Fragment's LoaderCallbacks.
         */
        LinkedHashMap<String, List<Movie>> videoLists = prepareData();
        return videoLists;
    }

    @Override
    protected void onStartLoading() {
        //super.onStartLoading();
        forceLoad();
    }

    private LinkedHashMap<String, List<Movie>> prepareData() {
        LinkedHashMap<String, List<Movie>> videoLists = new LinkedHashMap<>();
        List<Movie> videoList = MovieProvider.getMovieItems();
        videoLists.put("category 1", videoList);
        videoLists.put("category 2", videoList);
        videoLists.put("category 3", videoList);
        return videoLists;
    }
}

數(shù)據(jù)加載過程已經(jīng)實(shí)現(xiàn)。 最后,回到Activity側(cè)來處理這些數(shù)據(jù),在UI上顯示。

回到Loader側(cè)

最后一個(gè)任務(wù)是僅實(shí)現(xiàn)onLoadFinished回調(diào)。 參數(shù)是來自Loader的加載數(shù)據(jù)。
MainFragment.java

        @Override
        public void onLoadFinished(Loader<LinkedHashMap<String, List<Movie>>> loader, LinkedHashMap<String, List<Movie>> data) {
            Log.d(TAG, "onLoadFinished");
            /* Loader data has prepared. Start updating UI here */
            switch (loader.getId()) {
                case VIDEO_ITEM_LOADER_ID:
                    Log.d(TAG, "VideoLists UI update");

                    /* Hold data reference to use it for recommendation */
                    mItems = new ArrayList<Movie>();

                    mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());

                    int index = 0;
                    /* GridItemPresenter */
                    HeaderItem gridItemPresenterHeader = new HeaderItem(index, "GridItemPresenter");
                    index++;

                    GridItemPresenter mGridPresenter = new GridItemPresenter();
                    ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(mGridPresenter);
                    gridRowAdapter.add(GRID_STRING_ERROR_FRAGMENT);
                    gridRowAdapter.add(GRID_STRING_GUIDED_STEP_FRAGMENT);
                    gridRowAdapter.add(GRID_STRING_RECOMMENDATION);
                    gridRowAdapter.add(GRID_STRING_SPINNER);
                    mRowsAdapter.add(new ListRow(gridItemPresenterHeader, gridRowAdapter));

                    /* CardPresenter */
                    CardPresenter cardPresenter = new CardPresenter();

                    if (null != data) {
                        for (Map.Entry<String, List<Movie>> entry : data.entrySet()) {
                            ArrayObjectAdapter cardRowAdapter = new ArrayObjectAdapter(cardPresenter);
                            List<Movie> list = entry.getValue();

                            for (int j = 0; j < list.size(); j++) {
                                Movie movie = list.get(j);
                                cardRowAdapter.add(movie);
                                mItems.add(movie);           // Add movie reference for recommendation purpose.
                            }
                            HeaderItem header = new HeaderItem(index, entry.getKey());
                            index++;
                            mRowsAdapter.add(new ListRow(header, cardRowAdapter));
                        }
                    } else {
                        Log.e(TAG, "An error occurred fetching videos");
                    }
                    /* Set */
                    setAdapter(mRowsAdapter);
            }
        }

Build and run

loader

您可以在github上檢查源代碼,但在UI上沒有什么改變,因?yàn)榧虞d程序?qū)⒃诤笈_執(zhí)行加載過程。 當(dāng)我們需要可能需要很長時(shí)間的后臺數(shù)據(jù)加載過程時(shí),出現(xiàn)Loader的優(yōu)勢。 作為后臺數(shù)據(jù)加載的一個(gè)例子,我將在下一章中介紹Internet中的數(shù)據(jù)加載。

注釋:Loader在另一個(gè)Activity中

我們已經(jīng)在MainFragment中實(shí)現(xiàn)了Loader,以及如何在另一個(gè)Activity/Fragment中實(shí)現(xiàn)Loader,例如VideoDetailsFragment 。 我認(rèn)為如果我們可以使用與不同的Activity相同的Loader實(shí)例是很好的。 實(shí)際上MainFragment和VideoDetailsFragment都需要相同的VideoList數(shù)據(jù)! 然而,對于我來說,我們不能在Activity/Fragment中共享Loader的實(shí)例是非常令人失望的事實(shí),因?yàn)長oaderManager不是系統(tǒng)級的單例,而是在每個(gè)Activity/Fragment存在實(shí)例( LoaderManagerImpl mLoaderManager )。

那么我們應(yīng)該如何在Activity之間管理數(shù)據(jù)呢? 我認(rèn)為一種方法是建立一個(gè)保持?jǐn)?shù)據(jù)實(shí)例的獨(dú)立數(shù)據(jù)管理器類。 這也在下一章完成。

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,247評論 6 543
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,520評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,362評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,805評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,541評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,896評論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,887評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,062評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,608評論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,356評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,555評論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,077評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,769評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,175評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,489評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,289評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,516評論 2 379

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