今天将对android开发文档中的的Loaders这篇文章进行翻译。
Loaders:加载器。在译文中加载器依然用loader来表示。
Introduced in Android 3.0, loaders make it easy to asynchronously load data in an activity or fragment. Loaders have these characteristics:
- They are available to every
Activity
andFragment
. - 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.
*对于每一个Activity和Fragment都是有效的
*它们提供异步加载数据的功能。
*它们监视它们的数据资源,并在这些资源发生变化时发送新的结果。
*当配置信息发生改变后重新被创建时它们会重连到上一次装载机的指示位置。而且,它们不需要重新查询数据。
Loader API Summary 加载器API概述:
There are multiple classes and interfaces that may be involved in using loaders in an application. They are summarized in this table:
在一个应用中使用加载器时会有多个类和接口会被包括进来。在下面这个表格中是它们的概述:
LoaderManager
An abstract class associated with anActivity
orFragment
for managing one or moreLoader
instances.This helps an application manage longer-running operations in conjunction with theActivity
orFragment
lifecycle; the most common use of this is with aCursorLoader
, however applications are free to write their own loaders for loading other types of data.
There is only oneLoaderManager
per activity or fragment. But aLoaderManager
can have multiple loaders.
关联到一个Activity或Fragment中的一个抽象类,用于管理一个或多个加载器实例。它用来帮助一个应用管理在Activity或Fragment中的耗时操作。它最常见的是和一个CursorLoader一起使用,即使应用可以自由地将其他类型的数据写入自己的加载器。
每一个Activity或Fragment都只有一个LoaderManager,但是一个LoaderManager可以包含多个加载器。
A callback interface for a client to interact with theLoaderManager
. For example, you use theonCreateLoader()
callback method to create a new loader.
一个为实现客户端与LoaderManager交互而提供的接口。例如,你可以使用onCreateLoader()回调方法去创建一个加载器。
An abstract class that performs asynchronous loading of data. This is the base class for a loader. You would typically useCursorLoader
, but you can implement your own subclass. While loaders are active they should monitor the source of their data and deliver new results when the contents change.
一个执行异步加载数据的抽象类。这是一个加载器的基类。你一般可以使用CursorLoader,但是你也可以实现自己的子类。当加载器被激活是它们用来监视它们的数据资源,和在内容改变时发送新的结果。
Abstract loader that provides anAsyncTask
to do the work.
一个提供异步任务来工作的抽象加载器。
A subclass ofAsyncTaskLoader
that queries theContentResolver
and returns aCursor
. This class implements theLoader
protocol in a standard way for querying cursors, building onAsyncTaskLoader
to perform the cursor query on a background thread so that it does not block the application's UI. Using this loader is the best way to asynchronously load data from aContentProvider
, instead of performing a managed query through the fragment or activity's APIs.
AsyncTaskLoader的子类用以查询ContentResolver同时返回一个Cursor对象。这个类实现了在查询游标标准方式方面的加载器协议,创建一个异步任务加载器在一个后台线程中执行游标查询不会造成应用线程的阻塞。使用这个加载器的最好方式是从一个ContentProvider中异步加载数据,而不是通过Fragment或Activity的接口来管理查询。
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 theLoaderManager
in order to initialize a loader and an implementation of aLoader
class such asCursorLoader
. The following sections show you how to use these classes and interfaces in an application.
上面表格中的类和接口是你在自己的应用中实现一个加载器的基本组件,你不需要为你创建的各个加载器全部包含它们,但是你需要一个LoaderManager的引用去初始化一个加载器和一个像CursorLoader这样的加载器的实现类。下面的部分将向你展示在一个应用中如何使用这些类和接口。
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:
这部分描述如何在一个应用中如何使用加载器,一个应用使用加载器通常包含下面这些部分:
- An
Activity
orFragment
. - An instance of the
LoaderManager
. - A
CursorLoader
to load data backed by aContentProvider
. Alternatively, you can implement your own subclass ofLoader
orAsyncTaskLoader
to load data from some other source. - An implementation for
LoaderManager.LoaderCallbacks
. This is where you create new loaders and manage your references to existing loaders. - A way of displaying the loader's data, such as a
SimpleCursorAdapter
. - A data source, such as a
ContentProvider
, when using aCursorLoader
. - 一个Activity或Fragment。
- 一个LoaderManager实例。
- 一个由一个ContentProvider返回的加载数据的CursorLoader。或者,你可以实现属于自己的Loader或AsyncTaskLoader的子类来从其他资源中加载数据。
- 一个LoaderManager.LoaderCallbacks的实现。这是你创建新的加载器和管理目前的加载器的引用的地方。
- 展示加载器的数据的方式,例如一个SimpleCursorAdapter。
- 一个数据源,例如一个内容提供者,当使用一个CursorLoader时。
Starting a Loader 开启一个加载器
TheLoaderManager
manages one or moreLoader
instances within anActivity
orFragment
. There is only oneLoaderManager
per activity or fragment.
LoaderManager在一个Acitivity或Fragment中管理一个或多个加载器实例。每一个Activity或fragment仅有一个LoaderManager。
You typically initialize aLoader
within the activity'sonCreate()
method, or within the fragment'sonActivityCreated()
method. You do this as follows:
一般在activity的onCreate方法中初始化一个加载器,或者fragment的onActivityCreate方法中。像下面这些做:
// Prepare the loader. Either re-connect with an existing one, // or start a new one. getLoaderManager().initLoader(0, null, this);
TheinitLoader()
method takes the following parameters:
- A unique ID that identifies the loader. In this example, the ID is 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. In this example, the local class implements theLoaderManager.LoaderCallbacks
interface, so it passes a reference to itself,this
.
这个InitLoader()方法需要下面几个参数:
标识加载器的唯一Id,在这个例子中Id是0。
提供给加载器的可选参数(在这个例子中是null)。
一个LoaderManager.loaderCallBacks实现,LoaderManager通过它报告加载器事件。在这个例子中,这个本地类实现了LoaderManager.LoaderCallbacks接口,所有它把自己的引用传了进去,this。
TheinitLoader()
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.
- If the loader specified by the ID doesnotexist,
initLoader()
triggers theLoaderManager.LoaderCallbacks
methodonCreateLoader()
. This is where you implement the code to instantiate and return a new loader. For more discussion, see the sectiononCreateLoader.
initLoader()的调用确保加载器被初始化和激活。它有两个可能的返回结果:
如果这个加载器指定的id已经存在,上一次被创建的加载器就被重用。
如果这个加载器指定的id不存在,initLoader()方法触发LoaderManager.LoaderCallbacks
方法onCreateLoader()。这是你实现实例化和返回一个新的加载器代码的地方。更多的讨论,参见onCreateLoader部分。
In either case, the givenLoaderManager.LoaderCallbacks
implementation is associated with the loader, and will be called when the loader state changes. 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 callsonLoadFinished()
immediately (duringinitLoader()
), so you must be prepared for this to happen. SeeonLoadFinishedfor more discussion of this callback
Note that theinitLoader()
method returns theLoader
that is created, but you don't need to capture a reference to it. TheLoaderManager
manages the life of the loader automatically. TheLoaderManager
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 theLoaderThrottlesample). You most commonly use theLoaderManager.LoaderCallbacks
methods to intervene in the loading process when particular events occur. For more discussion of this topic, seeUsing the LoaderManager Callbacks.
注意initLoader()方法返回的是已经被创建的加载器,但是你不需要获取对它的引用。LoaderManager自动管理加载器的生命。Loadermanager在必要的时候启动和停止加载,而且保持加载器的状态和它关联的内容。这意味着,你很少直接与加载器交互(通过使用加载器方法对加载器的行为进行调整的例子,查看LoaderTrottle例子)。当一个特别的事件发生时你通常使用LoaderManager.LoaderCallbacks的方法进行干预。此话题的更多讨论,查看Using the LoaderManager Callbacks部分。
Restarting a Loader 重启一个加载器
When you useinitLoader()
, as shown above, it uses an existing loader with the specified ID if there is one. 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 userestartLoader()
. For example, this implementation ofSearchView.OnQueryTextListener
restarts the loader when the user's query changes. The loader needs to be restarted so that it can use the revised search filter to do a new query:
为了清除你的老数据,你需要使用restartLoader()方法。例如,这个SearchView.OnQueryTextListener的实现在用户查询发生改变时重新启动加载器。那个加载器需要被重启来使它能够使用修改后的搜索过滤器做一个新的查询:
public boolean 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); return true; }
Using the LoaderManager Callbacks 加载器管理器回调的使用
LoaderManager.LoaderCallbacks
is a callback interface that lets a client interact with theLoaderManager
.
LoaderManager.LoaderCallbacks是一个让客户端和LoaderManager进行交互的回调接口。
Loaders, in particularCursorLoader
, are expected to retain their data after being stopped. This allows applications to keep their data across the activity or fragment'sonStop()
andonStart()
methods, so that when users return to an application, they don't have to wait for the data to reload. You use theLoaderManager.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.
加载器,在特别的CursorLoader中,在被停止后你期望保存它们的数据。这允许应用通过activity或fragment的onStop()和onStart()方法对它们的数据进行保持,所以当用户返回到一个应用时,他们不必等着数据被重新加载,当知道何时创建一个新的加载器时你使用LoaderManager.LoaderCallbacks 方法,告诉那个应用停止使用加载器的数据时间。
LoaderManager.LoaderCallbacks
includes these methods: LoaderManager.LoaderCallbacks包括这些方法:
-
onCreateLoader()
— Instantiate and return a newLoader
for the given ID.实例化和为给定id返回一个新的装载器。
-
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, throughinitLoader()
), it checks to see whether the loader specified by the ID exists. If it doesn't, it triggers theLoaderManager.LoaderCallbacks
methodonCreateLoader()
. This is where you create a new loader. Typically this will be aCursorLoader
, but you can implement your ownLoader
subclass.
当你试图访问一个装载器时(例如,通过initLoader()),它会检查那个装载器是否被已存在的id所指定。如果它没有,它将触发LoaderManager.LoaderCallbacks
的onCreateLoader()方法。这是你创建一个新的装载器的地方。通常它会是一个CursorLoader,但是你可以实现自己的装载器子类。
In this example, theonCreateLoader()
callback method creates aCursorLoader
. You must build theCursorLoader
using its constructor method, which requires the complete set of information needed to perform a query to theContentProvider
. Specifically, it needs:
在这个例子中,onCreateLoader()回调方法创建了一个CursorLoader。你必须使用它的构造方法来创建一个CursorLoader,它需要对ContentProvider执行一个查询所需要的全套的信息。特别地,它需要:
- uri— The URI for the content to retrieve.
- projection— A list of which columns to return. Passing
null
will return all columns, which is inefficient. - 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. - selectionArgs— You may include ?s in the selection, which will be replaced by the values fromselectionArgs, in the order that they appear in the selection. The values will be bound as Strings.
- 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.
uri--要检索内容的URI。
projection--要返回的某一列元素的列表。传递空将返回所有列的元素的集合,这是不高效的。
selection--声明要返回哪些行的过滤器,按照SQL的where语句格式化(包含where本身)。传递null将会返回给定URI的所有行。
selectionArgs--在Selection中可以包含多个?,它们将会被从selectionArgs中得到的值所替代,使它们显示在selection中。这些值将会被绑定为字符串。
sortOrder--如何对行进行排序,被格式化成SQL ORDER BY 子句(包含ORDER BY 本身)。传递空值将会使用默认排序,那也许是无序的。
(以上这些参数,对数据库有些了解的程序员来说应该容易理解一些。)
For example:例如:
// If non-null, this is the current filter the user has provided. String mCurFilter; ... public Loader<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. 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"); }
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 aCursorLoader
, you should not callclose()
on it yourself. If the cursor is being placed in aCursorAdapter
, you should use theswapCursor()
method so that the oldCursor
is not closed. For example:
一旦装载机知道应用程序不再使用那些数据就会释放它们。例如,如果数据是从一个CursorLoader返回的cursor,你自己就应该调用它的close()方法。如果游标被放置在一个CursorAdapter中,你应该使用swapCursor()方法以便旧的Cursor也被关掉。例如:
// This is the Adapter being used to display the list's data. SimpleCursorAdapter mAdapter; ... public void 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); }
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 callsswapCursor()
with a value ofnull
:
这个实现调用swapCursor同时传递一个空值。
// This is the Adapter being used to display the list's data. SimpleCursorAdapter mAdapter; ... public void 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 aFragment
that displays aListView
containing the results of a query against the contacts content provider. It uses aCursorLoader
to manage the query on the provider.
例如,这是一个fragment的完全实现,展示了一个从联系人内容提供者中获取的查询结果的listView。它使用一个CursorLoader来管理提供者上的查询。
For an application to access a user's contacts, as shown in this example, its manifest must include the permissionREAD_CONTACTS
.
一个访问用户通讯录的应用,如这个例子所示,它的清单文件必须包含READ_CONTACTS权限。
public static class CursorLoaderListFragment extends ListFragment implements OnQueryTextListener, 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; @Override public void 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 = 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); // Prepare the loader. Either re-connect with an existing one, // or start a new one. getLoaderManager().initLoader(0, null, this); } @Override public void 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 = new SearchView(getActivity()); sv.setOnQueryTextListener(this); item.setActionView(sv); } public boolean 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); return true; } @Override public boolean onQueryTextSubmit(String query) { // Don't care about this. return true; } @Override public void 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. 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) { // 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. 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) { // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) mAdapter.swapCursor(data); } public void 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 inApiDemosthat illustrate how to use loaders:
在ApiDemos中有几个不同的例子介绍了如何使用装载机:
- LoaderCursor— 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 when its data changes.
For information on downloading and installing the SDK samples, seeGetting the Samples.
关于下载和安装SDK例子的信息,参见Getting the Smaples。
至此这篇文档已经翻译完毕,希望对你有所帮助,水平有限,错误之处敬请见谅!