CursorLoaders有什么好处?

时间:2011-08-24 22:09:00

标签: android cursor android-3.0-honeycomb android-loadermanager

我在我的应用程序中广泛使用Cursors来加载和偶尔从数据库写入信息和向数据库写入信息。我已经看到Honeycomb和兼容包有新的Loader类,旨在帮助以“良好”的方式加载数据。

基本上,这些新类(特别是CursorLoader)是否比以前的数据管理方法好得多?例如,CursorLoader优于托管Cursors有什么好处?

我使用ContentProvider来处理数据,这显然需要Uris,但这与initLoader()方法有何关系?我必须设置每个Fragments以单独使用装载程序吗?并且每个加载器的id需要多么独特,它是在我的应用程序范围内还是仅仅是一个片段?有没有简单的方法可以简单地将Uri传递给CursorLoader来查询我的数据?

目前我所能看到的是,Loaders添加了一个不必要的额外步骤来将我的数据导入我的应用程序,那么有人可以更好地向我解释这些吗?

2 个答案:

答案 0 :(得分:44)

在您的应用中使用CursorLoader优于Activity.managedQuery()有两个主要好处:

  1. 查询是在后台线程上为您处理的(由AsyncTaskLoader构建),因此大型数据查询不会阻止UI。这是文档建议您在使用普通Cursor时为自己做的事情,但现在已经在幕后完成了。
  2. CursorLoader正在自动更新。除了执行初始查询外,CursorLoader还会在您请求的数据集中注册ContentObserver,并在数据集发生更改时自行调用forceLoad()。这导致您在数据更改时随时获取异步回调以更新视图。
  3. 每个Loader实例也通过单数LoaderManager处理,因此您仍然无需直接管理光标,现在连接可以持续超过单个ActivityLoaderManager.initLoader()LoaderManager.restartLoader()允许您重新连接已经为您的查询设置的现有Loader,并且在某些情况下,如果可用,会立即获取最新数据。

    您的ActivityFragment现在可能会实施LoaderManager.Callback界面。如果需要,调用initLoader()将导致onCreateLoader()方法构建查询和新的CursorLoader实例。每次有新数据时都会触发onLoadFinished()方法,并且会包含最新的Cursor,以便您附加到视图或以其他方式迭代。

    此外,在LoaderManager类文档页面上有一个非常好的例子: http://developer.android.com/reference/android/app/LoaderManager.html

    希望有帮助!

答案 1 :(得分:10)

如果有人发现自己处于类似的情况,那就是我所做的:

  • 创建了一个实现LoaderCallbacks的类,并处理所有您需要的查询。
  • 使用Context和问题Adapter提供此内容。
  • 为您将使用的每个查询创建唯一ID(如果您使用UriMatcher,也可以使用相同的ID)
  • 制作便捷方法,将查询转移到LoaderCallbacks
  • 所需的包中
  • 这就是它:)我在下面放了一些代码来准确显示我做了什么

在我的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会在Cursor
  • 中为适配器提供正确的onLoadFinished(..)
  • 接下来,我们告诉LoaderManager我们要初始化新的CursorLoader。我们提供了一个新的GlobalCallbacks实例(实现Loader.Callbacks),然后监视光标的加载。我们也必须为它提供适配器,因此它可以在完成加载后交换新的Cursor。在某些时候,LoaderManager(内置于操作系统中)将调用onCreateLoader(..)的{​​{1}}并开始异步加载数据
  • GlobalCallbacks只需将查询的参数放入Helper.bundleArgs(..)(例如列投影,排序顺序,WHERE子句)
  • 然后我们设置Bundle的{​​{1}}。此时光标仍为空,因此在调用Fragment之前它将显示加载符号或空消息