Introduced in Android 3.0, loaders make it easy to asynchronously load data in an activity or fragment. Loaders have these characteristics:在Android3.0包括进来,加载器使得在活动和碎片中更容易加载异步数据,它有这些特性:
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:在一个应用中使用加载器,可能有多个类和接口会被调用.它们总结于下表:
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.下面部分将告诉你如何在应用中使用这些类和接口
This section describes how to use loaders in an Android application. An application that uses loaders typically includes the following:这部分描述如何在Android应用中使用加载器,使用加载器的应用一般包括如下部分:
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()
这个方法带有如下这些参数
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:该方法调用以确保一个加载器是被初始化和激活的,它有两个可能的输出结果:
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 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.
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.这是为了对内容提供者
ContentProvider
进行查询所需要的一套完整信息
Specifically, it needs:特别是,它需要"
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.