1. 裝載器API概要
2. 在應用中使用裝載器
2.1 啓動裝載器
2.2 重啓裝載器
3. 實例
3.1 更多實例
在android 3.0中引入了裝載器,它使得在活動或碎片內異步加載數據變得容易。裝載程序有如下特性:
- 它們對每個活動和碎片都是可用的。
- 它們提供異步的數據加載。
- 它們監控着活動和碎片的數據源,並在數據改變時遞送新結果。
- 配置改變後,當活動和碎片被重建時,它們自動重新連接到最後裝載器的遊標。因此,活動和碎片不必再次查詢它們的數據。
1. 裝載器API概要
在應用裏使用裝載器的過程中可能有多個類和接口被調用。它們被總結在下面的這個表中:
類/接口 |
描述 |
|
與 每個活動或碎片僅有一個 |
與 |
|
執行異步數據加載的抽象類。它是裝載器的基類。通常,你將使用 |
|
提供一個 |
|
|
上表中的類和接口是你在應用中用來實現裝載器的基本組件。你不需要所有這些爲你創建的每個裝載機,但是你通常需要一個LoaderManager引用以便初始化
裝載器以及Loader
類的實現,譬如CursorLoader
。下面的章節展示在應用中如何使用這些類和接口。
2. 在應用中實例裝載器
本章節描述在Android應用中如何使用裝載器。使用裝載器的應用基本上包括以下部分:
Activity
或Fragment
.LoaderManager
實例.CursorLoader
,以加載由ContentProvider
提供的數據。 作爲選擇,你可以實現你自己的Loader或AsyncTaskLoader
的子類來加載來自其他數據源的數據。LoaderManager.LoaderCallbacks
的實現。這就是你創建新的裝載器以及管理現有裝載器引用的地方。- 顯示裝載器數據的方式,比如
SimpleCursorAdapter
。 - 數據源,比如
ContentProvider
, 當使用CursorLoader
時。
2.1 啓動裝載器
在
管理一個或多個Activity
或Fragment裏,
LoaderManagerLoader
實例。
每個活動或碎片僅有一個
。LoaderManager
通常,在活動的onCreate()
方法內初始化Loader
或者是在碎片的onActivityCreated()
方法內。像下面這樣做:
// 準備裝載器。重連一個現有的裝載器,或者啓動一個新的。
getLoaderManager().initLoader(0, null, this);
initLoader()
方法攜帶以下參數:
- 唯一的ID,它標識了裝載器。在這個例子中,ID爲0。
- 可選的參數,在構造器中提供給裝載器(本例中爲null)。
一個LoaderManager.LoaderCallbacks
實現,LoaderManager
調用它來報告裝載器事件。本例中,本地類實現了LoaderManager.LoaderCallbacks
接口,所以它專遞了一個自身的引用,即this。
initLoader()調用確保了裝載器被初始化且是激活的。
此調用可能有兩種結果:call ensures that a loader is initialized and active. It has two possible out comes:
- 如果由ID指定的裝載器已經存在,則最後創建的裝載器被再次使用。
- 如果由ID指定的裝載器不存在,
initLoader()
將觸發LoaderManager.LoaderCallbacks
方法onCreateLoader()
。這就是你實現代碼來初始化並返回一個新的裝載器的地方。更多討論,查閱onCreateLoader章節。
在任何一種情況下,給定的LoaderManager.LoaderCallbacks
實現與裝載器是相關聯的,並且當裝載器狀態改變時被調用。如果在此調用點裝載器處於它的啓動狀態,且請求的裝載器已經存在,那麼系統則立即調用onLoadFinished()
(在initLoader()
期間),所以你必須爲這種情的出現做好準備。更多次回調的討論,請查閱onLoadFinished。
注意, initLoader()
方法返回被創建的Loader
,但是你不需要獲得它的引用。LoaderManager
自動管理着裝載器的生命。LoaderManager啓動並在必要時停止加載
,而且維持着裝載器的狀態和它有關聯的內容。這意味着,很少與裝載器直接交互(不過對於使用裝載器方法來微調裝載器行爲的例子,請查閱LoaderThrottle實例)。當特定事件發生時,通常你必須使用
LoaderManager.LoaderCallbacks
方法來介入加載過程。對於更多該主題的討論,請查閱使用LoaderManager回調章節。
2.2 重啓裝載器
如上所示,當使用initLoader()
時,它使用一個具有指定ID的現有裝載器,如果存在一個的話。如果沒有,則創建一個。不過有時你打算丟棄舊的數據然後重新開始。
爲了丟棄舊數據,你需要使用restartLoader()
。例如,下面這個SearchView.OnQueryTextListener
實現在用戶的查詢數據發生變化時重啓了裝載器:
public boolean onQueryTextChanged(String newText) {
// 在當動作欄搜索文本發生變化時被調用。
// 更新搜索過濾,然後重啓裝載器用這個過濾器來執行一條新的查詢。
mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
getLoaderManager().restartLoader(0, null, this);
return true;
}
2.3 使用LoaderManager回調
LoaderManager.LoaderCallbacks
是一個允許客戶端與
LoaderManager交互的回調接口。
在特殊的
CursorLoader
內,裝載器被希望用來在客戶端被停止後獲取他們的數據。這允許應用跨越活動或碎片的onStop()和onStart()方法來保持它們的數據,所以當用戶返還到應用時,他們不必等待數據重新加載。當知道何時創建一個新的裝載器時,使用
LoaderManager.LoaderCallbacks
接口方法,然後告訴應用何時停止使用裝載器的數據。
LoaderManager.LoaderCallbacks
接口包括這些方法:
onCreateLoader()
— 實例化一個新的指定ID的
。Loader,然後返回它
onLoadFinished()
— 當先前創建的裝載器完成它的加載時調用。
onLoaderReset()
— 當先前的裝載器正在重置時調用,因此使得它的數據不可用。
這些方法在接下來的章節裏有更爲詳細地描述。
onCreateLoader
當試圖訪問裝載器時(例如,通過initLoader()
),它來檢查通過制定ID的裝載器是否存在。如果不存在,它觸發LoaderManager.LoaderCallbacks
的onCreateLoader()
方法。這就是創建新的裝載器的地方。通常,這可能是CursorLoader
,但是你可以實現你自己的Loader
子類。
在此例中,Loader回調方法創建一個
CursorLoader
。創建它必須使用CursorLoader
的構造函數,它要求一套完整的需要來執行ContentProvider查詢的信息。它尤其需要:
- uri — 檢索內容的URI。
- projection — 返回哪些列的清單。傳遞null將返回所以列,但效率不高。
- selection — 一個聲明返回哪些行的過濾器,其格式如同SQL WHERE子句(不包括WHERE本身)。傳遞null將返回給定URI的全部行。
- selectionArgs — 在selection中可以包含 ?符號,這將被
的值所替代,它們按順序出現在來自selectionArgs
中。這些值限制爲String。selection
- sortOrder — 如何讀行排序,格式如同SQL ORDER BY字句(不包括ORDER BY本身)。傳遞null將使用默認的存儲順序,它可能是無序的。
例如:
// 如果非空,它是用戶提供的當前過濾器。
String mCurFilter;
...
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// 在需要創建一個新的裝載器時調用它。
// 該例子中只有一個裝載器,所以不用在乎ID。
// 首先,依當前我們是否正在過濾來選擇基準URI來用。
Uri baseUri;
if (mCurFilter != null) {
baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
Uri.encode(mCurFilter));
} else {
baseUri = Contacts.CONTENT_URI;
}
// 現在創建並返回一個CursorLoader,它將負責創建正被顯示的數據遊標。
String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("
+ Contacts.DISPLAY_NAME + " != '' ))";
return new CursorLoader(getActivity(), baseUri,
CONTACTS_SUMMARY_PROJECTION, select, null,
Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
}
onLoaderReset
當先前創建的裝載器正在被重置時調用這個方法,因此致使它的數據不可用。通過此函數可以知道數據何時將要被釋放,這樣你就可以移除你對它的引用。
下面的實現以一個null值調用
:swapCursor()
// 這是用來顯示列表數據的適配器。
SimpleCursorAdapter mAdapter;
...
public void onLoaderReset(Loader<Cursor> loader) {
//當最後提供給onLoadFinished()的Cursor將要被關閉時被調用。我們需要確保不再使用它。
mAdapter.swapCursor(null);
}
3. 實例
作爲實例,下面是一個
的完整實現,它顯示了一個Fragment
,其包含有對聯繫人內容提供者進行查詢的結果ListView
。實現使用
來管理在提供者上的查詢。CursorLoader
對於一個訪問用戶聯繫人的應用,如此例所示,它的清單文件中必須包括
權限。READ_CONTACTS
public static class CursorLoaderListFragment extends ListFragment
implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {
// 用來顯示列表數據的適配器。
SimpleCursorAdapter mAdapter;
// 如果非空,這是用戶提供的當前過濾器。
String mCurFilter;
@Override public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// 如果沒有數據,提供一些文本來顯示。
// 在真正的應用中這可能出自於資源。
setEmptyText("No phone numbers");
//在動作欄顯示一個菜單項
setHasOptionsMenu(true);
//創建一個空的適配器,我們將使用它來顯示被加載的數據。
mAdapter = new SimpleCursorAdapter(getActivity(),
android.R.layout.simple_list_item_2, null,
new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
new int[] { android.R.id.text1, android.R.id.text2 }, 0);
setListAdapter(mAdapter);
//準備裝載器。要麼與一個現有的裝載器重新連接,要麼啓動一個新的裝載器。
getLoaderManager().initLoader(0, null, this);
}
@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
//放置一個動作欄項用來搜索。
MenuItem item = menu.add("Search");
item.setIcon(android.R.drawable.ic_menu_search);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
SearchView sv = new SearchView(getActivity());
sv.setOnQueryTextListener(this);
item.setActionView(sv);
}
public boolean onQueryTextChange(String newText) {
//當動作欄搜索文本改變時調用。
// 更新搜索過濾器,並重啓裝載器用這個過濾器來執行新的查詢。
mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
getLoaderManager().restartLoader(0, null, this);
return true;
}
@Override public boolean onQueryTextSubmit(String query) {
//不要在乎它。
return true;
}
@Override public void onListItemClick(ListView l, View v, int position, long id) {
// 這裏插入期望的行爲。
Log.i("FragmentComplexList", "Item clicked: " + id);
}
//這些是將要取回的聯繫人數據行。
static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
Contacts._ID,
Contacts.DISPLAY_NAME,
Contacts.CONTACT_STATUS,
Contacts.CONTACT_PRESENCE,
Contacts.PHOTO_ID,
Contacts.LOOKUP_KEY,
};
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// 當需要創建一個新的裝載器時調用它。本例中僅有一個裝載器,所以不必在乎ID。
// 首先,依當前是否正在過濾,選擇基準URI來用。
Uri baseUri;
if (mCurFilter != null) {
baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
Uri.encode(mCurFilter));
} else {
baseUri = Contacts.CONTENT_URI;
}
// 現在創建並返回一個CursorLoader,它將負責創建正被顯示的數據的遊標.
String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("
+ Contacts.DISPLAY_NAME + " != '' ))";
return new CursorLoader(getActivity(), baseUri,
CONTACTS_SUMMARY_PROJECTION, select, null,
Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// 交換新的遊標。 (一旦返回,框架將負責關閉老的遊標)。
mAdapter.swapCursor(data);
}
public void onLoaderReset(Loader<Cursor> loader) {
// 當提供給onLoadFinished()的最後的遊標將要關閉時調用。
// 需要確保我們不在使用它。
mAdapter.swapCursor(null);
}
}
3.1 更多實例
在ApiDemos中有很多不同的例子,它們說明了如何使用裝載器:
- LoaderCursor — 如上所示代碼段的完整版。
- LoaderThrottle — 一個如何使用節流來減少內容提供者執行查詢的數目,當它的數據發生變化時。
更多下載好安裝SDK實例的信息,請參閱 Getting the Samples 。