在 Android 中使用 Cursor Loader






4.76/5 (10投票s)
本文档有助于理解在 Android 中使用 Cursor Loader。
介绍
Loader 的概念是在 Android 3.0 (API 级别 11) 中引入的。Loader 的特性可以列举如下:
- Loader 主要用于在非 UI 线程上为 Activity 或 Fragment 提供数据的异步加载。虽然应用程序应该从主线程调用 Loader,但 Loader (或 Loader 的子类) 在单独的线程中执行其工作,并将结果传递回主线程。
- 代码实现不应直接派生自
android.content.Loader
类,而应专门派生自android.content.CursorLoader
类。 - 在 Activity 或 Fragment 中加载数据的不同阶段会调用 Loader 的回调。简而言之,Activity 或 Fragment 需要实现 Listener 才能使用 Loader。
- Loader 内部使用
AsyncTask
来执行数据加载。与 AsyncTask 相比,Loader 没有性能优势,前提是 AsyncTask 被设计和开发得当。 - Loader,更具体地说,
CursorLoader
在后台线程中查询Content Resolver
,这样应用程序的用户界面就不会被阻塞,并将加载的 Cursor 返回给 Activity 或 Fragment。 - CursorLoader 实现查询游标的 Loader 协议。
CursorLoader
处理游标的生命周期。在使用 CursorLoader 时,开发者不应调用游标的 close() 方法。- Loader 会持久化获取的数据,以避免对简单的 Activity 刷新事件(如方向更改、键盘打开等)进行重复的获取操作。
- Loader 会监视其数据源,并在内容更改时传递新结果。它会在配置更改后重新创建时自动重新连接到最后一个 Loader 的游标,从而无需重新查询其数据。换句话说,
CursorLoader
会自动更新,因此无需重新查询游标。 - Loader,特别是
CursorLoader
,预计会在停止后保留其数据。这允许应用程序在 Activity 或 Fragment 的onStop()
和onStart()
方法之间保留其数据,这样当用户返回到应用程序时,他们不必等待数据重新加载。 - Loader 是兼容库的一部分。因此,开发者可以在 Android HoneyComb 之前的应用程序中使用它。
- 从 Android 3.0 开始,开发者应该使用
CursorLoader
而不是Activity.managedQuery
或Activity.startManagingCursor
。 - 每个 Activity 或 Fragment 只有一个
LoaderManager
。LoaderManager
会自动管理 Activity 或 Fragment 内的一个或多个 Loader 实例的生命周期。
使用 Loader 的应用程序通常包含以下内容:
- 一个 Activity 或 Fragment。
- 一个
LoaderManager
实例。 - 一个
CursorLoader
:用于加载由ContentProvider
支持的数据。或者,开发者可以自由实现自己的 Loader 或AsyncTaskLoader
子类来加载其他类型的数据。 LoaderManager.LoaderCallbacks
的实现。 这是一个回调接口,允许客户端与LoaderManager
进行交互。- 一种显示 Loader 数据的方式,例如
SimpleCursorAdapter
。 - 数据源,例如
ContentProvider
(在使用CursorLoader
时)。
在 Android < 3.0 中使用 Cursor Loader
- 开发者需要使用兼容库:https://developer.android.com.cn/tools/extras/support-library.html
- 按照以下说明进行设置: https://developer.android.com.cn/sdk/compatibility-library.html#SettingUp
- Activity 必须扩展
FragmentActivity
。您可能需要导入以下类:
import android.support.v4.app.FragmentActivity; import android.support.v4.app.LoaderManager;
如果您使用 SimpleCursorAdapter
,请确保替换此行
import android.widget.SimpleCursorAdapter;
用这个:
import android.support.v4.widget.SimpleCursorAdapter;
编写代码的步骤
public class MyActivity implements LoaderManager.LoaderCallbacks<Cursor>
loadermanager.initLoader(1, null, this);
- Activity 需要实现
LoaderManager.LoaderCallbacks
- 初始化 Loader
- 实现 Loader 回调方法。
onCreateLoader
:为给定的 ID 实例化并返回一个新的 Loader。这是创建游标的地方。onLoadFinished
:在之前的 Loader 完成加载后调用。在这里,您可以开始使用游标。onLoaderReset
:在之前的 Loader 被重置,使其数据不可用时调用。它被重置是为了创建一个新的游标来查询不同的数据。这在上面onLoadFinished()
提供的最后一个游标即将关闭时调用。我们需要确保我们不再使用它。
如何使用 Loader 的完整代码如下:
package tpg.main;
import tpg.database.DatabaseAccessUtility;
import tpg.database.DatabaseHandler;
import android.app.ListActivity;
import android.app.LoaderManager;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
import android.util.Log;
import android.widget.SimpleCursorAdapter;
public class MainActivity extends ListActivity implements LoaderCallbacks<Cursor> {
SimpleCursorAdapter mAdapter;
LoaderManager loadermanager;
CursorLoader cursorLoader;
private static String TAG="CursorLoader";
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
loadermanager=getLoaderManager();
String[] uiBindFrom = { DatabaseHandler.UserTable.name};
int[] uiBindTo = {android.R.id.text1};
/*Empty adapter that is used to display the loaded data*/
mAdapter = new SimpleCursorAdapter(this,android.R.layout.simple_list_item_1, null, uiBindFrom, uiBindTo,0);
setListAdapter(mAdapter);
/**
* This initializes the loader and makes it active. If the loader
* specified by the ID already exists, the last created loader is reused.
* If the loader specified by the ID does not exist, initLoader() triggers
* the LoaderManager.LoaderCallbacks method onCreateLoader().
* This is where you implement the code to instantiate and return a new loader.
* Use restartLoader() instead of this, to discard the old data and restart the Loader.
* Hence, here the given LoaderManager.LoaderCallbacks implementation are associated with the loader.
*/
loadermanager.initLoader(1, null, this);
}
/**
* This creates and return a new Loader (CursorLoader or custom Loader) for the given ID.
* This method returns the Loader that is created, but you don't need to capture a reference to it.
*/
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
String[] projection = { DatabaseHandler.UserTable.id, DatabaseHandler.UserTable.name };
/**
* This requires the URI of the Content Provider
* projection is the list of columns of the database to return. Null will return all the columns
* selection is the filter which declares which rows to return. Null will return all the 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 determines the order of rows. Passing null will use the default sort order, which may be unordered.
* To back a ListView with a Cursor, the cursor must contain a column named _ID.
*/
cursorLoader = new CursorLoader(this, DatabaseAccessUtility.CONTENT_URI, projection, null, null, null);
return cursorLoader;
}
/**
* Called when a previously created loader has finished its load. This assigns the new Cursor but does not close the previous one.
* This allows the system to keep track of the Cursor and manage it for us, optimizing where appropriate. 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 framework would take of closing of old cursor once we return.
*/
public void onLoadFinished(Loader<Cursor> loader,Cursor cursor) {
if(mAdapter!=null && cursor!=null)
mAdapter.swapCursor(cursor); //swap the new cursor in.
else
Log.v(TAG,"OnLoadFinished: mAdapter is null");
}
/**
* This method is triggered when the loader is being reset and the loader data is no longer available.
* 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.
*/
public void onLoaderReset(Loader<Cursor> arg0) {
if(mAdapter!=null)
mAdapter.swapCursor(null);
else
Log.v(TAG,"OnLoadFinished: mAdapter is null");
}
}
上面的代码将在 Listview 中显示 DatabaseHandler.UserTable.name 数据库表中的条目。