安卓翻译 Activities->Loaders

来源:互联网 发布:linux如何安装软件 编辑:程序博客网 时间:2024/04/29 09:57

在安卓3.0中被介绍,loaders使在一个activity或者fragment中异步装载数据容易完成。Loaders有这些特点:

他们对每个Activity和Fragment都是可用的。

他们提供异步的数据装载。

他们管理数据源和当内容改变时传递新结果。

当一个配置改变后他们被重新创建时,他们自动重新连接左后一个loader的游标。因此,他们不需要重新查询他们的数据。


Loader API 概要


这儿有多个类和接口可能在一个应用使用loaders所涉及到的。它们用下面的表格来简要介绍:

类/接口描述LoaderManager一个与Activity或者Fragment相关联的管理一个或者多个Loader实例的抽象类。这帮助应用管理那些与Activity或者Fragment生命周期联接的长时间操作;这个大多数是和CursorLoader一起使用的,然而应用不用写它们自己的loaders来装载其它数据类型。

每个activity或者fragment仅有一个LoaderManager。但是一个LoaderManager能有多个loaders。LoaderManager.LoaderCallbacks对客户端来说是一个与LoaderManager交互的回调接口。例如,你使用onCreateLoader()回调方法来创建一个新的loader。Loader执行异步数据装载的抽象类。这是对一个loader来说的基类。你可能经常使用CursorLoader,但是你能实现你自己的子类。当loaders活跃时,他们应该管理自己的数据源和当内容变化时传递新结果。AsyncTaskLoader给一个work提供一个异步任务的抽象loader。CursorLoaderAsyncTaskLoader的一个子类查询ContentResolver和返回一个Cursor。这个类以一种标准的方式实现Loader草案在查询游标,建立AsyncTaskLoader来在后台线程中执行游标查询以便它不会堵塞应用UI。使用这个loader是从一个ContentProvider中异步装载数据的最好方式,代替通过fragment或者activity的APIs中来执行一个托管查询。




















在上面表格中的类和接口是在应用中你将要用来实现一个loader的最基本的组件。对每一个你创建的loader你并需要上面的所有,但是你经常需要一个对LoaderManager的引用

为了初始化一个loader和一个Loader例如CursorLoader的一个实现。下面的章节展示了你如何在应用中使用这些类和接口


在应用中使用Loaders

这个章节描述了在安卓应用中如何使用loaders。一个使用loaders的应用经常包括的有下面:

一个activity或者Fragment。

LoaderManager的一个实例。

一个CursorLoader下载被ContentProvider返回的数据。可选择的是,你能实现你自己的Loader或AsyncTaskLoader的子类来从一些其它资源下载数据。

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

展示loader的数据的一个方式,例如SimpleCursorAdapter。

一个数据源例如ContentProvider当使用CursorLoader。


开启一个Loader

在activity或者Fragment中LoaderManager管理一个或者更多的Loader实例。每个activity或者fragment仅有一个LoaderManager。你经常在activity的onCreate()方法中初始化一个Loader或者在fragment的onActivityCreated()方法中。你要下面这样来做:

// Prepare the loader.  Either re-connect with an existing one,// or start a new one.getLoaderManager().initLoader(0, null, this);
这个initLoader()有着下面的参数:

一个定义loader的独一无二的ID。在这个例子中,ID是0.

给loader提供的任意内容 at construction (在这个例子中是null)。

一个loaderManager.LoaderCallbacks实现,LoaderManager调用它来报告loader事件。在这个例子中,本地类实现了LoaderManager.LoaderCallbacks接口因此它给自己传递了一个自己的接口,this。


initLoader()调用时要确保一个loader被初始化过和活跃的。它又两个可能发生的结果:

如果被ID指定的loader已经存在,那最后被创建的loader被重用。

如果被ID指定的loader不存在,那么initLoader()出发LoaderManager.LoaderCallbacks的方法onCreateLoader()。这里也是你使代码生效来实例化和返回一个新的loader的地方。更多讨论,观看章节onCreateLoader。


在任意一个例子中给出的LoaderManager.LoaderCallbacks实现与loader相关联,并且当loader状态改变时将会被调用。如果在调用的这个时间内调用者正在它的启动状态,而且被请求的loader已经存在并且声称了它的数据,那么系统直接调用onLoadFinished()(在initLoader()过程中)因此你必须为这个的发生来做准备。


注意initLoader()方法返回被创建的Loader但是你不需要捕获一个对它的引用。LoaderManager自动管理loader的生命。LoaderManager在需要时启动和停止装载,并且维护loader的状态和它相关联的内容。这就意味着,你很少与loaders直接交互(尽管对一个使用loader方法来调整loader的行为的的例子,但是请看LoaderThrottle样例)。当特殊事件发生时,你通常都用LoaderManager.LOaderCallbacks方法来干涉装载进程。关于这节的更多讨论内容,请看使用LoaderManager Callbacks。


重启一个Loader


当你像上面展示的那样来使用initLoa(),它会使用一个被指定了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 回调

LoaderManager.LoaderCallbacks 是一个让客户端与LoaderManager交互的回调接口。


Loader,尤其是CursorLoader,都会在被停止后保存它们的数据。这允许应用保留它们的数据,在activity或者fragment的onStop()和onSta()方法中,以便当用户返回应用时,他们不需要等待数据重新加载。当你知道在什么时候创建一个新的loader时,使用LoaderManager.LoaderCallbacks方法,并且当是时候停止使用loader数据时告诉应用。


LoaderManager.LoaderCallbacks包括下面这些方法:

onCreateLoader() -- 对给定的ID初始化并返回一个新Loader。

onLoadFinished() --- 当前一个被创建的loader完成了它的装载之后被调用

onLoaderReset() --- 当前一个被创建的loader正在被重置时被调用,因此使它的数据不可用。

这些方法在接下来的章节中会被详细讨论

onCreateLoader

当你尝试获取一个loader(例如,通过initLoad())时,它会检查被ID指定的loader是否已经存在。如果它没有的话,它会触发LoaderManager.LoaderCallbacks方法中的onCreateLoader()。这是你创建新的loader的方法。通常情况下,这回事CursorLoader,但是你能实现你自己的Loaderz子类。


在这个例子中,onCreateLoader()回调函数创建了一个CursorLoader。你必须使用它的构造方法来建立CursorLoader,这个构造函数请求被需要用来对ContentProvider执行一次查询的完整信息集合。特别指出的是,它需要:

uri --- 提取内容的URI

projection -- 列返回的一个列表。传递null将会返回所有列,这样是没有效率的。

selection -- 一个声明哪一列被返回的过滤器,它被像一个SQL WHERE子句一样被格式化(不包括WHERE本身)。传递null将会返回被给出的URI的所有行。

selectionArgs --- 你可能在选择集合中包括?,这会被从selectionArgs的值所代替,为了它们出现在selection中。值会像Strings一样被捆绑。

sortOrder -- 怎样排序行,这被像一个SQL ORDER BY 子句一样被格式化(不包括ORDER BY本身)。传递null会使用默认排序,这可能会是无序的。


例如

// 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的最后的数据被释放之前肯定会被调用。在这时,你应该移走所有的旧数据(在它被释放不久),但是你不在应该来做你自己对数据的释放,由于它的loader还拥有它并且小心照顾它。

loader将会释放数据一旦它知道应用不再使用它时。例如,如果数据时从CursorLoader来的游标,你不应该自己来调用close()。如果游标被放置在一个CursorAdapter,你应该使用swapCursor()方法,以便旧游标不被关闭。例如:

// 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被重置时,这个方法被调用,因此使它的数据不可用。这个方法使你发现数据何时将会被释放,因此你就能移除对它的引用。


这个实现调用带有null值得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);}

例子

作为一个例子,这是一个fragment的完整实现来呈现一个包含对通讯录content provider的一次查询结果的ListView。它用CursorLoader来管理在provider的查询。


对一个获取用户的通讯录的应用而言,就如在例子中展示的那样,你的manifest文件必须包括权限 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);    }}
更多例子

在ApiDemos中还有一些其它的例子来展示怎样使用loaders:

LoaderCursor -- 上面展示片段的完整版本。

loaderThrottle --- 一个怎样使用节流器来减少当一个content provider的数据改变时它所查找的次数的例子。