API Guides > Loaders

前言:

     1、原文链接:http://developer.android.com/guide/components/loaders.html

     2、如果上面链接访问不了,可以访问国内镜像链接:http://wear.techbrood.com/guide/components/loaders.html

     3、另外,别人翻译的一系列关于LoadManager的文件,挺好的,值得看:http://blog.csdn.net/murphykwu/article/details/35287883

----------------------------------------------分割线----------------------------

Loaders


Loaders在Android3.0开始引入,它简化了 activity(或者fragment)异步加载数据的方法。Loaders有一下这些特性:

1、在任何Activity或Fragment中,都可以使用

2、提供异步加载数据的方法

3、监听数据的变化,当数据变化的时候,传递新的数据到客户端

4、在configuration改变的时候,自动重连到最新的loader的cursor,这样就避免重复查询数据。

(注:loader加载数据之后,保存数据,并监听数据的变化。如果数据没有发生变化,再次请求数据的话,就不需要重新查询数据,直接返回保存的数据)


Loader API Summary

Loaders涉及到很多个类和接口,总结如下表:

类/接口 描述
LoaderManager虚类,关联到一个Activity或Fragment,用来管理一个或多 个Loader实例。
可以帮助应用管理管理数据的生命周期,让数据的生命周期和Activity或
Fragment一样。经常和CursorLoader一起使用。当然,你也可定义自
己的loader去加载任意类型的数据。每一个Activity或Fragment只有一个
LoaderManager,但是一个LoaderManager可以有多个loader。
LoaderManager.LoaderCallbacksLoaderManager的回调接口,用来让客户端和LoaderManager交互。
例如,可以在onCreateLoader()回调函数里创建一个新的loader。
Loader虚类,用来异步加载数据。这是loader的基类。经常使用的子类是
CursorLoader,当然,可以自己实现Loader的子类。当loader启动
之后,需要监听数据的变化,在数据更新的时候,更新数据给客户端。
AsyncTaskLoader虚类,用一个AsyncTask去异步加载数据
CursorLoaderAsyncTaskLoader的子类,从ContentResolver查询数据,并返回一个
Cursor。这个类实现Loader定义的接口,以标准的方式查询cursor。继
承自AsyncTaskLoader,使用后台进程去查询cursor,所以不会UI堵塞。
从ContentProvider查询数据最好是使用这个类,而不是使用Activity或
Fragment的managed query的API

上表的类和接口是用来实现loader的基本组成部分。在实现loader的时候,不需要用到所有的类或接口。但是LoaderManager的必要的,用来初始化loader。Loader的实现也是必要的,可以直接使用CursorLoader。下面的章节将要讲如何使用这些类和接口。


Using Loaders in an Application
这个章节描述如何使用loaders。使用loaders通常需要包含以下几点:

1、一个Activity或Fragment

2、一个LoaderManager的实例

3、一个CursorLoader,用来加载ContentProvider的数据。当然,如要你需要加载ContentProvider之外的数据,可以自己继承Loader或者AsnycTaskLoader,实现loader类。

4、实现r LoaderManager.LoaderCallbacks。这些回调可以用来创建新的loader,管理loader的引用。

5、显示loader的数据,例如SimpleCursorAdapter

6、一个数据源,如ContentProvider(当使用CursorLoader的时候)。


Starting a Loader
Activity或Fragment的LoaderManager管理一个或多个Loader实例。每一个Activity或Fragment只有一个LoaderManager。
通常可以再Activity的onCreate中或者Fragment的onActivityCreateed中,来初始化Loader。以下代码是初始化的方法:


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


initLoader() 有以下几个参数:

1、唯一的ID用来表示Loader。在上面的例子中,ID是0;

2、可选的参数,传递给Loader的构造函数。在上面的例子中,值的null

3、一个LoaderManager.LoaderCallbacks的实现,LoaderManager根据 Loader的状态调用这些回调函数。在上面的例子中,在该类中实现了LoaderManager.LoaderCallbacks的接口,所以就把该类自己的引用this作为参数。

调用initLoader()可以保证loader已经被初始化,并且是活动的。它可能会有两种情况:

1、该ID定义的loader已经存在,则最后创建的loader会被复用。

2、该ID定义的loader不存在,initLoader()会触发LoaderManager.LoaderCallbacks回调中的onCreateLoader()。在onCreateLoader()中,需要编写代码创建一个新的loader。在onCreateLoader的章节中,有更多关于这个函数的讨论。


不管哪种情况,LoaderManager.LoaderCallbacks的实现和loader相关联,当loader的状态变化的时候,就会被回调。如果调用者处于started的状态,并且指定ID的loader已经纯在,还查询到了数据,这样子,在调用initLoader的时候,系统会马上回调onLoaderFinish()。因此,你必须为这种情况做好准备。在onLoaderFinish()的章节中,有更多的讨论。

调用initLoader()后,会得到一个loader的索引,但是没有必要存储这个索引。因为LoaderManager会帮你管理loader。LoaderManager会在必要的时候启动或停止一个Loader,并且保存loader的状态和loader相关的数据。这意味着,你很少需要和loader交互(尽管在LoaderThrottle 的例子中,直接调用laoder的方法可以更好的调整loader的行为,具体查看LoaderThrottle 的例子)。常用的使用方法是,使用LoaderManager.LoaderCallbacks 的回调去参与loading数据的过程。更多的讨论,参考sing the LoaderManager Callbacks这个章节。


Restarting a Loader
当你使用InitLoader(),就像上个章节介绍的,如果指定ID的loader已经存在的话,就会使用存在的那个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;
}

Using the LoaderManager Callbacks

LoaderManager.LoaderCallbacks是一个回调接口,用来让客户端和LoaderManager交互的。

Loader,特别是CursorLoader,需要在停止之后还能保存数据。这样才能保证,当activity或fragment在执行onStop和onStart方法后,用户重新回到应用,不需要等待数据的加载。LoaderManager.LoaderCallbacks的回调告知客户端,何时创建一个loader,何时停止使用loader的数据。

LoaderManager.LoaderCallbacks包含以下方法:

onCreateLoader() — 创建并返回一个给定ID的loader。
onLoadFinished() — loader加载完数据后的回调。
onLoaderReset() — loader被重置(reset)后的回调,这时候就要弃用这个loader的数据。

在下面的章节有对这些方法更详细的描述。

onCreateLoader

当你要访问一个loader的时候(例如,调用initLoader()),先检查给定的loader ID是否已经存在。如果不存在,则会触发LoaderManager.LoaderCallbacks 的onCreateLoader()。这是创建一个新的loader的地方。通常,是用来创建一个CursorLoader。当然,你可以实现自己的loader。

在这个例子中,onCreateLoader()里创建了一个CursorLoader,你要用CursorLoader的构造函数来创建这个CursorLoader,构造函数的参数包含完整的查询ContentProvider的信息,通常,它需要这些参数:

1、uri---查询数据的uri

2、projection----定义那些需要返回数据的列。如果为空的话,会返回所有的列,这样会比较没有效率。

3、selection----过滤条件,格式和SQL语言的WHERE clause(不包含WHERE)相似。如果为空的话,将会返回给定URI的所有行。

4、selectionArgs----在selection中可能会包含“?”,这些“?”会被selectionArgs中的值替代。替代的顺序和他们在selection出现的顺序一致。这些值都以字符串的形式表示。

5、sortOrder----返回数据的排列顺序。格式和SQL的ORDER BY clause(不包含ORDER BY)相似。如果为空 的话,就会使用默认的排序,默认排序可能是没有排序。
例子:

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

在之前创建的loader加载完数据后,就会回调这个函数。这个方法比释放loader的旧数据更早调用(译者注:是否在restart a laoder的时候,会出现这种情况?)。所以,你要移除所有的旧数据(虽然旧数据很快会被移除),但是不要释放掉loader的数据,因为loader自己会管理数据。
当loader知道应用程序不再需要的时候,自己回去释放掉数据。例如,用CursorLoader回去一个cursor的时候,你不必调用cursor的close方法来关闭cursor。如果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
当之前创建的loader被重置(reset)的时候,这个方法会被回调,在这里,不要再使用loader的数据。这个方法让你知道,数据何时被释放,你要取消对数据的引用。

相面代码的实现,使用swapCursor(null):

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
下面的例子,完整实现用一个Fragment来显示一个ListView, ListView从contact provicder获取数据。它使用CursorLoader来provider的查询。
如果要访问联系人数据,需要加入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 thatillustrate how to use loaders:

  • LoaderCursor — A complete version of thesnippet shown above.
  • LoaderThrottle — An example of how to use throttling toreduce the number of queries a content provider does when its data changes.

For information on downloading and installing the SDK samples, see Getting theSamples





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值