Android Loader 官方教程翻译

Loaders

目录

1.     Loader API 简介

2.     在应用中使用Loader

1.     启动一个Loader

2.     重启一个Loader

3.     使用Loader管理器LoaderManager 的回调方法

3.     实例

1.     更多实例

主类

1.     LoaderManager

2.     Loader

相关

1.     LoaderCursor

2.     LoaderThrottle

 


 

Loader是在Android 3.0引入的一个API,使用LoaderActivityFragment中异步加载数据会变得更加容易。Loaders具有下面这些特征:

·         在每个Activity  Fragment中都有效

·         提供异步加载数据

·         可监控数据源并在内容改变时传递新的结果

·         当配置更改,Loader被重写创建时,会自动重新连接到最近的游标处。因此,不需要重新发起数据请求。

Loader API 简介

有多个类和接口可能会在应用中使用到Loader。这些类或接口如下:

Class/Interface

描述

LoaderManager

被关联到一个Activity或者Fragment的抽象类,用于管理一个或多个Loader接口。帮助应用程序管理在Activity或者Fragment中耗时长的操作;最常见的是结合 CursorLoader 一起使用。在应用程序中可以自由地编写自己的Loader加载其他类型的数据。

    每一个Activity或Fragment只有一个LoaderManager,但是一个LoaderManage可以有多个Loader。

LoaderManager.LoaderCallbacks

    客户端与LoaderManager交互的回调接口。例如,你可以使用onCreateLoader() 回调方法创建一个新的Loader。

Loader

执行异步加载数据的抽象类。这是一个具体loader的基类。你可以使用具有代表性的CursorLoader,但你也可以实现你自己的子类。当Loader被激活,他们就会监测数据源并且当内容改变的时候返回新的结果。

AsyncTaskLoader

一个抽象loader。提供了一个AsyncTask 完成需要的操作。

CursorLoader

A subclass of AsyncTaskLoader that queries the ContentResolver and returns a Cursor. 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.   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.

 AsyncTaskLoader 的一个子类,用于获取ContentResolver 和返回一个 Cursor.这个类使用标准的方式实现了Loader,用于获取游标。继承AsyncTaskLoader 使其能在后台线程中执行获取游标的操作,不至于堵塞UI线程。从ContentProvider中异步加载数据的最佳方式就是使用这个类。

  

上面表格中列举的类和接口都是在应用程序中实现加载器loader的重要组成部分。创建每一个Loader的时候并不是都需要使用到这些类和接口,但是你总会需要一份LoaderManager 的参考,用于初始化一个loader和实现一个Loader,例如  CursorLoader. 下面的章节将会告诉你怎么在你的应用程序中使用这些类和接口。

在应用中使用加载器Loader

    这个章节介绍了怎么在一个Android应用中使用加载器Loader。使用加载器一般需要以下内容:

·         一个Activity  Fragment.

·         LoaderManager的一个实例。

·         一个 CursorLoader 用于从 ContentProvider加载数据。或者你可以实现Loader 或者 AsyncTaskLoader 的子类,用于从其他数据源加载数据。

·          LoaderManager.LoaderCallbacks. 的一个实现。这是创建新的loaders和引用已经存在的loaders的地方。

·         一种展示数据的方式,如SimpleCursorAdapter.

·         一个数据源,如,当使用 CursorLoader.的时候,使用ContentProvider

开启一个 Loader

LoaderManagerActivity或者Fragment中管理一个或多个Loader实例。一个ActivityFragment只有一个LoaderManager。通常需要在ActivityonCreate()方法或者FragmentonActivityCreated()方法中初始化一个Loader。示例如下:

// Prepare the loader.  Either re-connect with an existing one,
// or start a new one.
getLoaderManager
().initLoader(0, null, this);

initLoader() 的参数解析:

·         Loader的唯一ID.在这个例子中,ID0

·         构建loader时的可选参数。 (null in this example).

·         一个LoaderManager.LoaderCallbacks 的实现LoaderManager 调用这个实现来响应Loader的事件. 在这个例子中,当前的类实现了 LoaderManager.LoaderCallbacks 接口,所以传递了一个引用给自己。

.

调用 initLoader() 一个Loader被初始化并激活。存在两种可能的结果:

·         If the loader specified by the ID already exists, the last created loader is reused.如果指定的ID已经存在,那么最近创建的loader就被复用。

·         如果指定的ID不存在,initLoader() 触发LoaderManager.LoaderCallbacks方法  onCreateLoader().在这个方法里实例化并返回一个新的loader。更多的讨论,在章节 onCreateLoader

 

在这两种情况下,LoaderManager.LoaderCallbacks 的实现是跟loader相关联的,当Loader的状态改变时,这些回调方法将会被调用。当调用者在开启状态,且需要的Loader已经存在并已经获得数据,那么系统将会立即调用回调方法onLoadFinished() ,更多关于该回调方法的讨论,可查看onLoadFinished 部分。

需要注意的是,initLoader()方法创建并返回一个Loader,当你不需要去捕获它的引用。LoaderManager 会自动管理这个loade的生命周期。当需要的时候,LoaderManager会开启或停止加载数据,并维护loader的状态和关联的内容。这意味着,你很少会直接手动操作loader(这里有一个微调loader行为的例子,查看 LoaderThrottle 示例)。最经常使用 LoaderManager.LoaderCallbacks回调方法来干预加载过程中发生的特定事件。更多关于该主题的讨论,请查看Using the LoaderManager Callbacks

 

重启一个Loader

当你像上面提到的那样使用initLoader(),当参数中的ID已经存在,使用的是已经存在的loader,如果ID不存在,将会创建一个新的loader。但有时候,你不再需要原来的数据并重新开始。

丢弃旧的数据,使用restartLoader()。如下面的例子,当查询改变的时候,在SearchView.OnQueryTextListener 的实现中重启loader。该Loader将需要重新启动,以便它可以使用修订后的搜索过滤器做一个新的查询。

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;
}

使用 LoaderManager Callbacks(一组回调方法)

LoaderManager.LoaderCallbacks 是一个与 LoaderManager 交互的回调接口。

很多加载器,特别是CursorLoader,希望停止的时候能够保留它们数据。这个允许应用程序的ActivityFragment的状态在onStop()  onStart() 之间变化时保留数据。因此,当用户回到应用程序的时候,不需要等待数据加载。当你知道什么时候创建loader,并且知道什么时候停止使用loader的数据时,使用LoaderManager.LoaderCallbacks 回调方法。

LoaderManager.LoaderCallbacks 包括以下的回调方法:

·         onCreateLoader() — 从给定的ID中实例化并返回一个新的Loader.

·         onLoadFinished() — 当之前创建的loader完成数据加载后调用。

·         onLoaderReset() — Called when a previously created loader is being reset, thus making its data unavailable.当之前创建的loader被重置的时候调用,原来的数据会丢弃。

下面将对这些方法进行详细的介绍。

onCreateLoader

When you attempt to access a loader (for example, through initLoader()), it checks to see whether the loader specified by the ID exists. If it doesn't, it triggers the LoaderManager.LoaderCallbacks method onCreateLoader(). This is where you create a new loader. Typically this will be a CursorLoader, but you can implement your ownLoader subclass.

当你企图去访问一个loader的时候(比如,通过initLoader(),它会先判断给定IDloader是否存在,如果不存在,将会触发该方法创建一个新的Loader。这通常是一个CursorLoader,但是你也可以实现自己的Loader子类。

In this example, the onCreateLoader() callback method creates a CursorLoader. You must build theCursorLoader using its constructor method, which requires the complete set of information needed to perform a query to the ContentProvider. Specifically, it needs:

·         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 from selectionArgs, 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.

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.

当加载器不再使用数据的时候,它将会自己释放这些数据。例如,如果数据来自CursorLoader,你不应该自己调用close() 如果cursor被设置在一个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 calls swapCursor() with a value of null:

// 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 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.

For an application to access a user's contacts, as shown in this example, its manifest must include the permission 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 in ApiDemos that illustrate how to use loaders:

&middot;         LoaderCursor — A complete version of the snippet shown above.

&middot;         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, see Getting the Samples.

 


你可能感兴趣的:(Android Loader 官方教程翻译)