Loaders
来源:互联网 发布:创维电视机推荐 知乎 编辑:程序博客网 时间:2024/05/20 06:22
Loader在android 3.0 被引进,使得在activity或fragment异步下载数据更简单。具备一下特点:
1. 对每个activity和fragment都适合。
2. 提供异步下载数据
3. 与原数据保持同步,随时更新。
4. 当配置变化导致重新创建时,自动与最近的loader的连接。这样就不需要重新查找数据。
Loader API Summary (Loader API 总结)
下面列出有可能在使用Loader用到的类和接口。
上面列出的这些类是用于实现Loader重要的组建。对于你创建的每个Loader,你不需要所有上面的逐渐,但你必须有一个LoaderManager的引用以便用来初始化一个Loader,还必须有一个实现Loader的类,比如CursorLoader。下面演示如何使用这些类和接口。
Using Loaders in an Application (在一个应用中使用Loader)
这部分讲解在android应用中如何使用Loader,一个使用Loader的应用包含下面几个部分:
一个activity或fragment
一个LoaderManager实例
一个从ContentProvider下载数据的CursorLoader。你也可以自己实现Loader或者AsyncTaskLoader接口从其它数据源下载数据。
一个用来显示Loader的数据,通常使用SimpleCursorAdapter。
一个数据源,比如当使用CursorLoader时,通常数据源是ContentProvider。
Starting a Loader (启动一个Loader)
在一个activity或fragment中,LoaderManager管理一个或多个Loader实例。但在一个activity或fragment中,只有一个LoaderManager。
通常在activity的onCreate()函数中初始化一个Loader,或者在fragment的onActivityCreate()函数中:
// Prepare the loader. Either re-connect with an existing one,// or start a new one.getLoaderManager().initLoader(0, null, this);初始化函数initLoader()有以下几个参数:
1. 一个用来标识Loader的标识符,比如上面例子的ID是0。
2. 可选参数,用与Loader创建,在上面例子为null。
3. 实现LoaderManager.LoaderCallbacks的类,LoaderManager调用这个函数来报告相应的loader事件,这上面例子中,这个activity类实现了LoaderManager.LoaderCallback()接口,所以使用this作为参数。
初始化函数initLoader()保证了一个loader被初始化并被激活,调用这个函数后,通常有几下两个可能的结果:
如果指定ID的Loader存在,最后创建的Loader会被重新使用。
如果制定ID的Loader不存在,那么initLoader()将会触发LoaderManager.LoaderCallbacks的onCreateLoader()函数。在这个函数中你必须实例化一个Loader并返回这个Loader。详见onCreateLoader。
无论那种情况,给定的LoadManager.LoaderCallbacks的实现都和Loader相关,当Loader的状态变化是,里面的相应的函数会被调用。如果请求的Loader已经存在并有自己的数据,那么系统将会马上调用onLoadFInished()函数,所以你必须考虑到这种情况,详见onLoaderFinished()。
注意到函数initLoader()返回了创建的Loader,但是你不必创建一个这个Loader的引用。LoaderManager能够自动的管理Loader的生命过程。LoaderManager在必要的情况下会启动或停止下载,维持Loader的状态和它关联的Content。也就是说你很少会直接和Loader交互(除了使用Loader中的函数来微调Loader的行为,参见LoaderThrottle),大多数情况你都是使用LoaderManager.LoaderCallbacks的函数 来干预下载过程。更多信息参照Using the LoaderManager Callbacks。
Restarting a Loader (重启一个Loader)
正如前面所示,当你用通过initLoader()来创建Loader时,如果制定的Loader ID存在,那么将使用这个已经存在的Loader,如果不存在,再创建一个。当有时候你相想丢弃旧的数据并 从新开始。
通过函数restartLoader()来丢弃旧的数据。比如下面的例子中,通过在函数SearchView.OnQueryTextListener检查用户查找的内容是否变化,如果变了,那么重新启动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)
LoaderManager.LoaderCallbacks是一个回调接口,用户通过它与LoaderManager.Loaders(一般是CursorLoader)交互,这些Loader会在fragment或activity保存数据,即在activity或fragment的onStop()和onStart()间还是会保存数据,使得当用户返回到应用的使用不用等待数据下载。使用LoaderManager.LoaderCallbacks可以直到创建一个新的Loader,并告诉应用什么时候停止使用一个Loader的数据。
LoaderManager.LoaderCallbacks包括下面的函数:
onCreateLoader():实例化一个新的指定ID的LoaderManager并返回这个Loader。
onLoadFinished():当前一个创建的Loader完成下载时被调用
onLoaderReset():当前一个创建的Loader被重置时被调用,使得之前的数据无效。
下面详细描述这些函数的用法:
onCreateLoader
当你试图访问一个Loader时(比如通过initLoader()),这个函数会检查指定ID的Loader是否存在,如果没有,会触发LoaderManager.LoaderCallbacks中的函数onCreateLoader()。在这个函数里会创建一个新的Loader,通常是CursorLoader,但你也可以通过继承Loader来创建自己的Loader。
在下面的例子中,onCreateLoader()创建一个CursorLoader。在CursorLoader的构造函数中,你必须提供完整的信息以便在ContentProvider进行查找。需要下列信息:
uri:从制定的uri获取数据。
projection:一个列表,包含需要返回的列,如果为空,那么将返回所有的列。
selection:过滤器,用来过滤想要返回的行,用SQL WHERE语句(包含WHERE关键字),如果为空,将返回所有的列。
selectionArgs:你可以在selection包含?,这个?将会被selectionArgs取代,顺序跟出现在selection的顺序一样。这个值只能是字符串。
sortOrder:指定如何对返回的行排序,用SQL ORDERBY语句(包括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");}
onLoaderFinished
当之前创建的loader完成下载时调用这个函数。这个函数必须在释放有这个loader提供的数据之前被调用。在这个函数中,你必须移除所有对所有的旧数据的使用(因为这些数据待会要被释放),但不是移除数据(仅仅移除对旧数据的使用),因为数据放在loader中。
一旦直到应用不再使用这些数据,loader将会移除这些数据,比如,如果数据是在CursorLoader中,那么你不能调用CursorLoader的close()函数,如果此时这个cursor关联到CursorAdapter中,那么你只能调用swapCursor()来却换Cursor而不能直接关闭这个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被重置时这个函数被调用,使得之前的数据不可用。这个函数保证你能在数据被释放前能够移除所有对这个数据的引用。
通过调用swapCursor()并传入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);}
下面例子是用一个fragment来显示一个listview,这个listview包含从contact content provider查询的数据,其中使用了CursorLoader来对provider进行查询。
对于想访问用户contact的应用,必须在它的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); }}
More Example (更多例子)
在ApiDemos中还有更多关于如何使用的例子
LoaderCursor:上面例子的完整版
LoaderThrottle:一个用来说明如何使用throttling来减少当ContentProvider数据变化时的查询次数。
- Loaders
- Loaders
- Loaders
- Loaders
- Loaders
- Loaders
- loaders& linkers
- android Loaders
- Loaders 使用
- Android--Loaders
- Android--Loaders
- Android--Loaders
- 使用Loaders
- Activity--Loaders
- 实现Loaders
- webpack ------ loaders
- 什么是loaders?
- Webpack Loaders
- 用PS从图片上找配色方案
- Bug笔记:Google Map第一次缩放位置偏移
- 业务逻辑和算法
- 起初的挑食对于孩子来说是自然反应
- myeclipse手动安装android插件(摘抄)
- Loaders
- MYSQL通过Replication双机热备份
- 《南帝北丐2》决战襄阳 乱战江湖夺霸位
- 运用keil的ULINK2下的ITM机制打印调试信息
- 在x86 linux上porting driver 遇到的dma memory不一致的问题
- HTML之Position用法
- IE网页弹出窗口的参数
- ImageFun 使JPG的缩略图跟原图不一样 (刷微博必备)
- 【10.2移动新特性】揭秘ArcGIS 10.2移动产品的离线功能【下:服务模式】