我在我的应用程序中广泛使用Cursors
,以加载和偶尔从数据库写入信息。我看到Honeycomb和Compatibility Package有新的Loader
类,旨在帮助以"良好"的方式加载数据。
CursorLoader
)比以前的数据管理方法要好得多吗?例如,CursorLoader
相对于受管理的Cursors
有什么好处?我使用ContentProvider
来处理数据,这显然需要Uris
,但这如何与initLoader()
方法相匹配?我必须设置每个Fragments
单独使用加载器吗?每个加载器的id需要有多独特,它是在我的应用范围内还是只是一个片段?是否有任何简单的方法来传递Uri
到CursorLoader来查询我的数据?
我现在所能看到的是加载器添加了一个不必要的额外步骤来获取我的数据到我的应用程序中,所以有人能更好地向我解释吗?
在应用程序中使用CursorLoader
比使用Activity.managedQuery()
有两个主要好处:
- 查询是在后台线程上为您处理的(由
AsyncTaskLoader
构建),所以大数据查询不会阻塞UI。这是文档建议你在使用普通Cursor
时自己做的事情,但现在它已经在底层完成了。 -
CursorLoader
自动更新。除了执行初始查询外,CursorLoader
还向您请求的数据集注册ContentObserver
,并在数据集更改时调用forceLoad()
。这导致你在任何时候得到异步回调数据的变化,以更新视图。
每个Loader
实例也通过单个LoaderManager
处理,因此您仍然不必直接管理游标,现在连接甚至可以在单个Activity
之外持续存在。LoaderManager.initLoader()
和LoaderManager.restartLoader()
允许您重新连接已经为您的查询设置的现有Loader
,并且在某些情况下,如果可用,立即获得最新数据。
你的Activity
或Fragment
现在可能会实现LoaderManager.Callback
接口。调用initLoader()
将产生onCreateLoader()
方法,您将在其中构造查询和一个新的CursorLoader
实例(如果需要的话)。onLoadFinished()
方法将在每次有新数据可用时被触发,并且将包含最新的Cursor
,供您附加到视图或以其他方式迭代。
此外,在LoaderManager
类文档页面上有一个很好的例子:http://developer.android.com/reference/android/app/LoaderManager.html
如果有人发现自己处于类似的情况,以下是我所做的:
- 创建了一个类来实现
LoaderCallbacks
并处理所有你需要的查询。 - 提供
Context
和Adapter
。 - 为您将使用的每个查询创建唯一的id(如果您使用
UriMatcher
,不妨使用相同的) - 创建一个方便的方法,将查询传输到
LoaderCallbacks
所需的bundle中。 - 差不多了:)我把我的一些代码放在下面,以显示我到底做了什么
我的GlobalCallbacks
类:
public static final String PROJECTION = "projection";
public static final String SELECTION = "select";
public static final String SELECTARGS = "sargs";
public static final String SORT = "sort";
Context mContext;
SimpleCursorAdapter mAdapter;
public GlobalCallbacks(Context context, SimpleCursorAdapter adapter) {
mContext = context;
mAdapter = adapter;
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Uri contentUri = AbsProvider.customIntMatch(id);
if (contentUri != null) {
return new CursorLoader(mContext, contentUri, args.getStringArray(PROJECTION), args.getString(SELECTION),
args.getStringArray(SELECTARGS), args.getString(SORT));
} else return null;
}
@Override
public void onLoadFinished(Loader<Cursor> arg0, Cursor arg1) {
mAdapter.swapCursor(arg1);
}
@Override
public void onLoaderReset(Loader<Cursor> arg0) {
mAdapter.swapCursor(null);
}
当我想使用CursorLoader
(Helper.bundleArgs()
是方便的捆绑方法):
scAdapt = new Adapters.NewIndexedAdapter(mHost, getMenuType(),
null, new String[] { "name" }, new int[] { android.R.id.text1 });
getLoaderManager().initLoader(
GlobalCallbacks.GROUP,
Helper.bundleArgs(new String[] { "_id", "name" }),
new GlobalCallbacks(mHost, scAdapt));
setListAdapter(scAdapt);
public static Bundle bundleArgs(String[] projection, String selection, String[] selectionArgs) {
Bundle b = new Bundle();
b.putStringArray(GlobalCallbacks.PROJECTION, projection);
b.putString(GlobalCallbacks.SELECTION, selection);
b.putStringArray(GlobalCallbacks.SELECTARGS, selectionArgs);
return b;
}
希望这对其他人有帮助:)
编辑
更彻底地解释:
- 首先,初始化一个
Cursor
为空的适配器。我们不提供Cursor
,因为GlobalCallbacks
将在onLoadFinished(..)
中为适配器提供正确的 -
Helper.bundleArgs(..)
只是将查询的参数放入Bundle
(例如列投影,排序顺序,WHERE子句) - 然后设置
Fragment
的ListAdapter
。此时游标仍然为空,因此它将显示一个加载标志或空消息,直到onLoadFinished()
被调用 。
Cursor
。接下来,我们告诉LoaderManager
我们要初始化一个新的CursorLoader
。我们提供了一个新的GlobalCallbacks
实例(它实现了Loader.Callbacks
),它将监视游标的加载。我们也必须为它提供适配器,这样它就可以在加载完成后交换新的Cursor
。在某些时候,LoaderManager
(内置于操作系统中)将调用GlobalCallbacks
的onCreateLoader(..)
并开始异步加载数据