版權(quán)聲明:本文為博主原創(chuàng)翻譯文章,轉(zhuǎn)載請注明出處。
推薦:
歡迎關(guān)注我創(chuàng)建的Android TV 簡書專題,會定期給大家分享一些AndroidTv相關(guān)的內(nèi)容:
http://www.lxweimin.com/c/3f0ab61a1322
使用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使用情況匯總 。)
- 這與AsyncTask非常相似,因此我們可以異步地在后臺線程中準(zhǔn)備數(shù)據(jù),并獲得回調(diào)以在UI線程上更新UI。 它使我們能夠輕松開發(fā)“優(yōu)秀”軟件架構(gòu),從而減少UI線程的任務(wù)。
-
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是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
您可以在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ù)管理器類。 這也在下一章完成。
參考
-
Loader和LoaderManager (第1部分)
關(guān)注微信公眾號,定期為你推薦移動(dòng)開發(fā)相關(guān)文章。
songwenju