Introduced in Android 3.0, loaders make it easy to asynchronously load data in an activity or fragment. Loaders have these characteristics:在Android3.0包括進來,加載器使得在活動和碎片中更容易加載異步數據,它有這些特性:html
Activity
and Fragment
.可用於每一個Activity
和Fragment
There are multiple classes and interfaces that may be involved in using loaders in an application. They are summarized in this table:在一個應用中使用加載器,可能有多個類和接口會被調用.它們總結於下表:android
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.下面部分將告訴你如何在應用中使用這些類和接口api
This section describes how to use loaders in an Android application. An application that uses loaders typically includes the following:這部分描述如何在Android應用中使用加載器,使用加載器的應用通常包括以下部分:app
Activity
or Fragment
.一個活動,或者一個碎片LoaderManager
.一個加載管理器的實例CursorLoader
to load data backed by a ContentProvider
一個支持加載ContentProvider
內容提供者數據的CursorLoader
加載器. Alternatively, you can implement your own subclass of Loader
or AsyncTaskLoader
to load data from some other source.作爲一種選擇,你能夠實現Loader
的子類或者AsyncTaskLoader,
加載來自其餘源的數據LoaderManager.LoaderCallbacks
. This is where you create new loaders and manage your references to existing loaders.一個 LoaderManager.LoaderCallbacks
實現,在這兒建立加載器並管理你的已存在的加載器的引用SimpleCursorAdapter
.一個顯示加載數據的方法,好比SimpleCursorAdapter
ContentProvider
, when using a CursorLoader
.一個數據源,好比,當使用CursorLoader
時,則是一個ContentProvider
內容提供者,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()
這個方法帶有以下這些參數async
null
in this example).給加載器在構造時使用的可選參數(在本例子中是空)LoaderManager.LoaderCallbacks
implementation, which the LoaderManager
calls to report loader events.一個用於LoaderManager
加載管理器,調用來報告加載事件的LoaderManager.LoaderCallbacks
實現 In this example, the local class implements the LoaderManager.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:該方法調用以確保一個加載器是被初始化和激活的,它有兩個可能的輸出結果:ide
initLoader()
triggers the LoaderManager.LoaderCallbacks
method onCreateLoader()
. 若是由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 callbackpost
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.ui
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;
}
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 new Loader
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.下面將詳細討論這些方法.
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.這是爲了對內容提供者
進行查詢所須要的一套完整信息
Specifically, it needs:特別是,它須要"ContentProvider
null
will return all columns, which is inefficient.返回列表中的那一列,傳遞null,將返回全部列,這樣的話沒有效率.null
will return all rows for the given URI.返回那些行的過濾聲明,格式化像SQL WHERE語句(包括WHERE本身),傳遞null將返給定URI的全部行.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");
}
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);
}
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);
}
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);
}
}
There are a few different samples in ApiDemos that illustrate how to use loaders:在ApiDemos 中有更多關於如何使用加載器的例子
For information on downloading and installing the SDK samples, see Getting the Samples.關於下載和安裝SDK例子,參考 Getting the Samples.