Introduced in Android 3.0, loaders make it easy to asynchronously load data in an activity or fragment. Loaders have these characteristics:在Android3.0包括進來,加載器使得在活動和碎片中更容易加載異步數據,它有這些特性:
- They are available to every
Activity
andFragment
.可用於每個Activity
和Fragment
- They provide asynchronous loading of data.它們提供了異步數據的裝載
- They monitor the source of their data and deliver new results when the content changes.它們監視數據的源,並當內容改變時傳遞新結果
- They automatically reconnect to the last loader's cursor when being recreated after a configuration change. Thus, they don't need to re-query their data.在配置改變后,重新創建時,它們會自動重新聯接最后的加載器指針,因此,它們不需要重新查詢它們的數據
Loader API Summary加載器
There are multiple classes and interfaces that may be involved in using loaders in an application. They are summarized in this table:在一個應用中使用加載器,可能有多個類和接口會被調用.它們總結於下表:
Class/Interface | Description |
LoaderManager |
An abstract class associated with an Activity or Fragment for managing one or more Loader instances.一個關聯到一個Activity 活動和Fragment 碎片的抽象類,用於管理一個或多個Loader 加載器實例. This helps an application manage longer-running operations in conjunction with the Activity or Fragment lifecycle加載管理器結合Activity 活動和Fragment 碎片的生命周期,有助於應用管理長期運行的操作; the most common use of this is with a CursorLoader , however applications are free to write their own loaders for loading other types of data加載管理器最常用的是CursorLoader ,然而應用可以自由的寫它們自己的加載器,來加其他的數據類型. There is only one LoaderManager per activity or fragment每個活動或者碎片只能有一個LoaderManager 加載管理器. But a LoaderManager can have multiple loaders但一個LoaderManager 加載管理器可以有多個加載器. |
LoaderManager.LoaderCallbacks |
A callback interface for a client to interact with the LoaderManager 與LoaderManager 加載管理器交互的客戶端回調接口. For example, you use the onCreateLoader() callback method to create a new loader例如,你用onCreateLoader() 回調方法創建一個新的加載器. |
Loader |
An abstract class that performs asynchronous loading of data一個異步數據加載器. This is the base class for a loader這是加載器的基類. You would typically use CursorLoader , but you can implement your own subclass一般你可能用CursorLoader ,但你可以實現自己的子類. While loaders are active they should monitor the source of their data and deliver new results when the contents change當加載器被激活,它們應該監視它們的數據源,並當數據源改變時傳遞新的結果. |
AsyncTaskLoader |
Abstract loader that provides an AsyncTask to do the work提供一個AsyncTask 來處理工作的抽象加載器. |
CursorLoader |
A subclass of AsyncTaskLoader that queries the ContentResolver and returns a Cursor .查詢ContentResolver 內容解釋器並返回一個Cursor 指針,AsyncTaskLoader 類的子數 This class implements the Loader protocol in a standard way for querying cursors, building on AsyncTaskLoader to perform the cursor query on a background thread so that it does not block the application's UI.這個類實現了一個標准的方法來查詢指針(指示器),構建於AsyncTaskLoader 之上,在一個后台線執行指針查詢,所以不會阻塞應用的UI .Using this loader is the best way to asynchronously load data from a ContentProvider , instead of performing a managed query through the fragment or activity's APIs.使用這個加載器異步加載來自內容提供者的數據是一個最好的方法,而不是通過碎片和活動的APIs,執行一個管理隊列. |
The classes and interfaces in the above table are the essential components you'll use to implement a loader in your application上表中的類和接口,是你在應用中實現一個加載器至關重要的組件. You won't need all of them for each loader you create, but you'll always need a reference to the LoaderManager
in order to initialize a loader and an implementation of a Loader
class such as CursorLoader
. 你所創建的加載器,並不需要所有這些組件和接口,但是為了初始化一個加載器及實現一個像CursorLoader
這樣的Loader
加載器的類,你總是需要一個LoaderManager
加載管理器的一個引用。The following sections show you how to use these classes and interfaces in an application.下面部分將告訴你如何在應用中使用這些類和接口
Using Loaders in an Application在應用中使用加載器
This section describes how to use loaders in an Android application. An application that uses loaders typically includes the following:這部分描述如何在Android應用中使用加載器,使用加載器的應用一般包括如下部分:
- An
Activity
orFragment
.一個活動,或者一個碎片 - An instance of the
LoaderManager
.一個加載管理器的實例 - A
CursorLoader
to load data backed by aContentProvider
一個支持加載ContentProvider
內容提供者數據的CursorLoader
加載器. Alternatively, you can implement your own subclass ofLoader
orAsyncTaskLoader
to load data from some other source.做為一種選擇,你可以實現Loader
的子類或者AsyncTaskLoader,
加載來自其他源的數據 - An implementation for
LoaderManager.LoaderCallbacks
. This is where you create new loaders and manage your references to existing loaders.一個LoaderManager.LoaderCallbacks
實現,在這兒創建加載器並管理你的已存在的加載器的引用 - A way of displaying the loader's data, such as a
SimpleCursorAdapter
.一個顯示加載數據的方法,比如SimpleCursorAdapter
- A data source, such as a
ContentProvider
, when using aCursorLoader
.一個數據源,比如,當使用CursorLoader
時,則是一個ContentProvider
內容提供者,
Starting a Loader開始加載
The LoaderManager
manages one or more Loader
instances within an Activity
or Fragment
.在一個Activity
活動或者Fragment
碎片中,LoaderManager
加載管理器管理一個或多個Loader
加載器, There is only one LoaderManager
per activity or fragment.每個活動或者碎片中只有一個LoaderManager
加載管理器.
You typically initialize a Loader
within the activity's onCreate()
method, or within the fragment's onActivityCreated()
method. You do this as follows:你一般會在活動的onCreate()
方法中,或者在一個碎片的onActivityCreated()
方法中,初始化一個Loader
加載器.你會這樣做:
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0,null,this);
The initLoader()
method takes the following parameters:initLoader()
這個方法帶有如下這些參數
- A unique ID that identifies the loader. In this example, the ID is 0.標識加載器的唯一ID,在這個例子中,該ID是0
- Optional arguments to supply to the loader at construction (
null
in this example).給加載器在構造時使用的可選參數(在本例子中是空) - A
LoaderManager.LoaderCallbacks
implementation, which theLoaderManager
calls to report loader events.一個用於LoaderManager
加載管理器,調用來報告加載事件的LoaderManager.LoaderCallbacks
實現 In this example, the local class implements theLoaderManager.LoaderCallbacks
interface, so it passes a reference to itself,this
.在本例中局部內實現了LoaderManager.LoaderCallbacks
接口,所以它傳遞了它自己,this.
The initLoader()
call ensures that a loader is initialized and active. It has two possible outcomes:該方法調用以確保一個加載器是被初始化和激活的,它有兩個可能的輸出結果:
- If the loader specified by the ID already exists, the last created loader is reused.如果由ID指定的加載器已存在,那么最后創建的加載器將被重用
- If the loader specified by the ID does not exist,
initLoader()
triggers theLoaderManager.LoaderCallbacks
methodonCreateLoader()
. 如果由ID指定的加載器不存在,initLoader()
觸發LoaderManager.LoaderCallbacks的
onCreateLoader()
方法.This is where you implement the code to instantiate and return a new loader. For more discussion, see the section onCreateLoader.你在這兒,實現實例化和返回一個新的加載器,詳細討論請看onCreateLoader.
In either case, the given LoaderManager.LoaderCallbacks
implementation is associated with the loader, and will be called when the loader state changes無論那種情況,上面給定的LoaderManager.LoaderCallbacks
實現,都與加載器相關聯,並且當加載器的狀態改變時將被調用. If at the point of this call the caller is in its started state, and the requested loader already exists and has generated its data, then the system calls onLoadFinished()
immediately (during initLoader()
), so you must be prepared for this to happen. See onLoadFinished for more discussion of this callback
Note that the initLoader()
method returns the Loader
that is created, but you don't need to capture a reference to it.注意initLoader()
方法返回一個被創建的Loader
加載器,但你不需要捕獲該加載器的引用. The LoaderManager
manages the life of the loader automatically.加載管理器LoaderManager
自動管理加載器的生命. The LoaderManager
starts and stops loading when necessary, and maintains the state of the loader and its associated content,當必要時,加載管理器啟動或停止加載器,並維持加載器的狀態及與它相關的內容. As this implies, you rarely interact with loaders directly (though for an example of using loader methods to fine-tune a loader's behavior, see the LoaderThrottle sample). 這意味着,你很少需要直接與加載器交互(但做為一個使用加載器的舉例,微調了加載器的行為,請看 LoaderThrottle例子)You most commonly use the LoaderManager.LoaderCallbacks
methods to intervene in the loading process when particular events occur當某特殊事件發生時,你經常需要用LoaderManager.LoaderCallbacks
的方法來干涉加載過程. For more discussion of this topic, see Using the LoaderManager Callbacks.關於本主題的詳細討論,參考Using the LoaderManager Callbacks.
Restarting a Loader重啟加載器
When you use initLoader()
, as shown above, it uses an existing loader with the specified ID if there is one.如上所述,當你使用initLoader()
時,如果有一個加載器,它將使用由ID指定的已存在的加載器.If there isn't, it creates one.如果沒有的話,它將創建一個. But sometimes you want to discard your old data and start over.但有時候,你想扔掉舊的數據並重新開始
To discard your old data, you use restartLoader()
.要扔掉你的舊數據,你要用restartLoader()
方法 For example, this implementation of SearchView.OnQueryTextListener
restarts the loader when the user's query changes.比如這個例子,當用戶查詢改變時,SearchView.OnQueryTextListener
的實現,重啟了加載器. The loader needs to be restarted so that it can use the revised search filter to do a new query:這個加載器重新啟動,為了能使用改進的搜索過濾器進行新的查詢.
publicboolean onQueryTextChanged(String newText){
// Called when the action bar search text has changed. Update
// the search filter, and restart the loader to do a new query
// with this filter.
mCurFilter =!TextUtils.isEmpty(newText)? newText :null;
getLoaderManager().restartLoader(0,null,this);
returntrue;
}
Using the LoaderManager Callbacks使用加載管理器回調
LoaderManager.LoaderCallbacks
is a callback interface that lets a client interact with the LoaderManager
.LoaderManager.LoaderCallbacks
是一個回調接口,客戶端可以通過它與加載管理器LoaderManager
交互.
Loaders, in particular CursorLoader
, are expected to retain their data after being stopped.加載器,特別是CursorLoader
加載器,期望在被停止后,能駐留它們的數據 This allows applications to keep their data across the activity or fragment's onStop()
and onStart()
methods, so that when users return to an application, they don't have to wait for the data to reload.這使得應用,能保持它們的數據跨越活動或者碎片的onStop()
和onStart()
方法,以便當用戶返回某個應用時,它們不必等待重新加載它們的數據。 You use the LoaderManager.LoaderCallbacks
methods when to know when to create a new loader, and to tell the application when it is time to stop using a loader's data.你使用LoaderManager.LoaderCallbacks
方法,知道什么時創建一個新的加載器,並告訴加載器什么時候該停止使用加載器的數據.
LoaderManager.LoaderCallbacks
includes these methods:它包括這些方法
onCreateLoader()
— Instantiate and return a newLoader
for the given ID.為給定的ID,實例化並返回一個加載器Loader
onLoadFinished()
— Called when a previously created loader has finished its load.當前一個創建的加載器已完成了它的加載時,調用
onLoaderReset()
— Called when a previously created loader is being reset, thus making its data unavailable.當先前創建的加載器,正在復位時調用,並使它的數據不可用.
These methods are described in more detail in the following sections.下面將詳細討論這些方法.
onCreateLoader響應創建加載器
When you attempt to access a loader (for example, through initLoader()
), it checks to see whether the loader specified by the ID exists.當你嘗試訪問加載器(比如,通過initLoader()
),它會檢查由ID標識的加載器是否存在。 If it doesn't, it triggers the LoaderManager.LoaderCallbacks
method onCreateLoader()
.如果不存在,它將觸發LoaderManager.LoaderCallbacks
方法onCreateLoader().
This is where you create a new loader. Typically this will be a CursorLoader
, but you can implement your own Loader
subclass.你在這里創建新加載器,一般是CursorLoader加載器,但也可以實現自己的
Loader
子類.
In this example, the onCreateLoader()
callback method creates a CursorLoader
. 在這個例子中,onCreateLoader()
回調方法創建一個CursorLoader
.You must build the CursorLoader
using its constructor method, which requires the complete set of information needed to perform a query to the ContentProvider
.你必須用它的構造方法構造CursorLoader.這是為了對內容提供者
ContentProvider
進行查詢所需要的一套完整信息
Specifically, it needs:特別是,它需要"
- uri — The URI for the content to retrieve.提取內容的URI
- projection — A list of which columns to return. Passing
null
will return all columns, which is inefficient.返回列表中的那一列,傳遞null,將返回所有列,這樣的話沒有效率. - selection — A filter declaring which rows to return, formatted as an SQL WHERE clause (excluding the WHERE itself). Passing
null
will return all rows for the given URI.返回那些行的過濾聲明,格式化像SQL WHERE語句(包括WHERE自己),傳遞null將返給定URI的所有行. - selectionArgs — You may include ?s in the selection, which will be replaced by the values from selectionArgs, in the order that they appear in the selection. The values will be bound as Strings.你可能在selection中包括 ?s.它將被selectionArgs的值替代,它們將依次出現在selection中,這些值將邦定成字串(be bound as 應該是這意思吧)
- sortOrder — How to order the rows, formatted as an SQL ORDER BY clause (excluding the ORDER BY itself). Passing
null
will use the default sort order, which may be unordered.怎樣排序行,格式化成SQL ORDER BY(包括ORDER BY自己),傳遞null將使用默認順序,這樣的話可能是無序的.
For example:
// If non-null, this is the current filter the user has provided.
String mCurFilter;
...
publicLoader<Cursor> onCreateLoader(int id,Bundle args){
// This is called when a new Loader needs to be created. This
// sample only has one Loader, so we don't care about the ID.
// First, pick the base URI to use depending on whether we are
// currently filtering.
Uri baseUri;
if(mCurFilter !=null){
baseUri =Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
Uri.encode(mCurFilter));
}else{
baseUri =Contacts.CONTENT_URI;
}
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
Stringselect="(("+Contacts.DISPLAY_NAME +" NOTNULL) AND ("
+Contacts.HAS_PHONE_NUMBER +"=1) AND ("
+Contacts.DISPLAY_NAME +" != '' ))";
returnnewCursorLoader(getActivity(), baseUri,
CONTACTS_SUMMARY_PROJECTION,select,null,
Contacts.DISPLAY_NAME +" COLLATE LOCALIZED ASC");
}
onLoadFinished
This method is called when a previously created loader has finished its load. 當預先創建的加載器完成了它的加載時,調用本方法.This method is guaranteed to be called prior to the release of the last data that was supplied for this loader.這個方法保證會在,釋放本加載器的最后的數據前會調用。 At this point you should remove all use of the old data (since it will be released soon), but should not do your own release of the data since its loader owns it and will take care of that.在你在這一點移除所有用過的舊數據(因為它即將釋放),但你不應該釋放加載器捅有的和將要關心的數據。
The loader will release the data once it knows the application is no longer using it.一旦知道應用將不再使用該數據,加載器將釋放該數據 For example, if the data is a cursor from a CursorLoader
, you should not call close()
on it yourself. 比如,如果數據是一個CursorLoader的
cursor,你不應自己對其調用close()
方法.If the cursor is being placed in a CursorAdapter
, you should use the swapCursor()
method so that the old Cursor
is not closed.如果cursor將被放到一個CursorAdapter中,你
應使swapCursor()
方法,以便舊的Cursor
不被關閉. For example:
// This is the Adapter being used to display the list's data.這是一個用於顯示列表的數據的適配器
SimpleCursorAdapter mAdapter;
...
publicvoid onLoadFinished(Loader<Cursor> loader,Cursor data){
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)把新的cursor交換進來,一旦我們返回,框架將負責關閉舊的cursor.
mAdapter.swapCursor(data);
}
onLoaderReset
This method is called when a previously created loader is being reset, thus making its data unavailable.當預選創建的加載器正在復位時,調用本方法,因此使得它的數據不可用. This callback lets you find out when the data is about to be released so you can remove your reference to it. 這個回調讓你找出什么時候數據將被釋放,以便你能移除對它的引用
This implementation calls swapCursor()
with a value of null
:這個實現調用一個帶null值的swapCursor()方法.
// This is the Adapter being used to display the list's data.
SimpleCursorAdapter mAdapter;
...
publicvoid onLoaderReset(Loader<Cursor> loader){
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed. We need to make sure we are no
// longer using it.
mAdapter.swapCursor(null);
}
Example舉例
As an example, here is the full implementation of a Fragment
that displays a ListView
containing the results of a query against the contacts content provider.這是一個碎片的完全實現的例子,顯示一個列表視圖,該視圖是對通信錄內容提供者的查詢結果, It uses a CursorLoader
to manage the query on the provider.它用一個CursorLoader
管理對內容提供者的查詢.
For an application to access a user's contacts, as shown in this example, its manifest must include the permission READ_CONTACTS
.這是一個展示了訪問用戶通信錄的例子,它的manifest必須包括READ_CONTACTS
權限.
publicstaticclassCursorLoaderListFragmentextendsListFragment
implementsOnQueryTextListener,LoaderManager.LoaderCallbacks<Cursor>{
// This is the Adapter being used to display the list's data.
SimpleCursorAdapter mAdapter;
// If non-null, this is the current filter the user has provided.
String mCurFilter;
@Overridepublicvoid onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
// Give some text to display if there is no data. In a real
// application this would come from a resource.
setEmptyText("No phone numbers");
// We have a menu item to show in action bar.
setHasOptionsMenu(true);
// Create an empty adapter we will use to display the loaded data.
mAdapter =newSimpleCursorAdapter(getActivity(),
android.R.layout.simple_list_item_2,null,
newString[]{Contacts.DISPLAY_NAME,Contacts.CONTACT_STATUS },
newint[]{ android.R.id.text1, android.R.id.text2 },0);
setListAdapter(mAdapter);
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0,null,this);
}
@Overridepublicvoid onCreateOptionsMenu(Menu menu,MenuInflater inflater){
// Place an action bar item for searching.
MenuItem item = menu.add("Search");
item.setIcon(android.R.drawable.ic_menu_search);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
SearchView sv =newSearchView(getActivity());
sv.setOnQueryTextListener(this);
item.setActionView(sv);
}
publicboolean onQueryTextChange(String newText){
// Called when the action bar search text has changed. Update
// the search filter, and restart the loader to do a new query
// with this filter.
mCurFilter =!TextUtils.isEmpty(newText)? newText :null;
getLoaderManager().restartLoader(0,null,this);
returntrue;
}
@Overridepublicboolean onQueryTextSubmit(String query){
// Don't care about this.
returntrue;
}
@Overridepublicvoid onListItemClick(ListView l,View v,int position,long id){
// Insert desired behavior here.
Log.i("FragmentComplexList","Item clicked: "+ id);
}
// These are the Contacts rows that we will retrieve.
staticfinalString[] CONTACTS_SUMMARY_PROJECTION =newString[]{
Contacts._ID,
Contacts.DISPLAY_NAME,
Contacts.CONTACT_STATUS,
Contacts.CONTACT_PRESENCE,
Contacts.PHOTO_ID,
Contacts.LOOKUP_KEY,
};
publicLoader<Cursor> onCreateLoader(int id,Bundle args){
// This is called when a new Loader needs to be created. This
// sample only has one Loader, so we don't care about the ID.
// First, pick the base URI to use depending on whether we are
// currently filtering.
Uri baseUri;
if(mCurFilter !=null){
baseUri =Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
Uri.encode(mCurFilter));
}else{
baseUri =Contacts.CONTENT_URI;
}
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
Stringselect="(("+Contacts.DISPLAY_NAME +" NOTNULL) AND ("
+Contacts.HAS_PHONE_NUMBER +"=1) AND ("
+Contacts.DISPLAY_NAME +" != '' ))";
returnnewCursorLoader(getActivity(), baseUri,
CONTACTS_SUMMARY_PROJECTION,select,null,
Contacts.DISPLAY_NAME +" COLLATE LOCALIZED ASC");
}
publicvoid onLoadFinished(Loader<Cursor> loader,Cursor data){
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
mAdapter.swapCursor(data);
}
publicvoid onLoaderReset(Loader<Cursor> loader){
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed. We need to make sure we are no
// longer using it.
mAdapter.swapCursor(null);
}
}
More Examples更多例子
There are a few different samples in ApiDemos that illustrate how to use loaders:在ApiDemos 中有更多關於如何使用加載器的例子
- FragmentListCursorLoader — A complete version of the snippet shown above.顯示在上面片斷代碼的完全版本
- LoaderThrottle — An example of how to use throttling to reduce the number of queries a content provider does then its data changes.一個展示了當數據改變時,怎樣使用throttling減少對內容提供者的查詢次數的例子
For information on downloading and installing the SDK samples, see Getting the Samples.關於下載和安裝SDK例子,參考 Getting the Samples.