107

Android SDK 文档说该startManagingCursor()方法已弃用:

此方法已弃用。将新的 CursorLoader 类与 LoaderManager 一起使用;这也可以通过 Android 兼容包在旧平台上使用。此方法允许活动根据活动的生命周期为您管理给定游标的生命周期。也就是说,当活动停止时,它会自动在给定的光标上调用 deactivate(),当它稍后重新启动时,它会为你调用 requery()。当活动被销毁时,所有托管游标将自动关闭。如果您的目标是 HONEYCOMB 或更高版本,请考虑改用 LoaderManager,可通过 getLoaderManager() 获得

所以我想用CursorLoader. 但是,当我在构造函数中需要 URI 时,如何使用自定义CursorAdapter和不使用它?ContentProviderCursorLoader

4

5 回答 5

155

我写了一个不需要内容提供者的简单 CursorLoader :

import android.content.Context;
import android.database.Cursor;
import android.support.v4.content.AsyncTaskLoader;

/**
 * Used to write apps that run on platforms prior to Android 3.0. When running
 * on Android 3.0 or above, this implementation is still used; it does not try
 * to switch to the framework's implementation. See the framework SDK
 * documentation for a class overview.
 *
 * This was based on the CursorLoader class
 */
public abstract class SimpleCursorLoader extends AsyncTaskLoader<Cursor> {
    private Cursor mCursor;

    public SimpleCursorLoader(Context context) {
        super(context);
    }

    /* Runs on a worker thread */
    @Override
    public abstract Cursor loadInBackground();

    /* Runs on the UI thread */
    @Override
    public void deliverResult(Cursor cursor) {
        if (isReset()) {
            // An async query came in while the loader is stopped
            if (cursor != null) {
                cursor.close();
            }
            return;
        }
        Cursor oldCursor = mCursor;
        mCursor = cursor;

        if (isStarted()) {
            super.deliverResult(cursor);
        }

        if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
            oldCursor.close();
        }
    }

    /**
     * Starts an asynchronous load of the contacts list data. When the result is ready the callbacks
     * will be called on the UI thread. If a previous load has been completed and is still valid
     * the result may be passed to the callbacks immediately.
     * <p/>
     * Must be called from the UI thread
     */
    @Override
    protected void onStartLoading() {
        if (mCursor != null) {
            deliverResult(mCursor);
        }
        if (takeContentChanged() || mCursor == null) {
            forceLoad();
        }
    }

    /**
     * Must be called from the UI thread
     */
    @Override
    protected void onStopLoading() {
        // Attempt to cancel the current load task if possible.
        cancelLoad();
    }

    @Override
    public void onCanceled(Cursor cursor) {
        if (cursor != null && !cursor.isClosed()) {
            cursor.close();
        }
    }

    @Override
    protected void onReset() {
        super.onReset();

        // Ensure the loader is stopped
        onStopLoading();

        if (mCursor != null && !mCursor.isClosed()) {
            mCursor.close();
        }
        mCursor = null;
    }
}

它只需要AsyncTaskLoader类。Android 3.0 或更高版本中的那个,或者兼容包附带的那个。

我还写了一个ListLoader与 兼容的LoadManager并用于检索泛型java.util.List集合。

于 2011-09-14T20:07:09.537 回答
23

编写您自己的加载器,该加载器使用您的数据库类而不是内容提供者。最简单的方法就是CursorLoader从兼容性库中获取类的源代码,并将提供者查询替换为对您自己的 db 助手类的查询。

于 2011-08-25T02:54:07.350 回答
14

SimpleCursorLoader 是一个简单的解决方案,但是它不支持在数据更改时更新加载器。CommonsWare 有一个 loaderex 库,它添加了一个 SQLiteCursorLoader 并支持对数据更改的重新查询。

https://github.com/commonsguy/cwac-loaderex

于 2012-06-22T17:58:51.373 回答
12

第三种选择是简单地覆盖loadInBackground

public class CustomCursorLoader extends CursorLoader {
    private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver();

    @Override
    public Cursor loadInBackground() {
        Cursor cursor = ... // get your cursor from wherever you like

        if (cursor != null) {
            // Ensure the cursor window is filled
            cursor.getCount();
            cursor.registerContentObserver(mObserver);
        }

        return cursor;
    }
};

这也将负责在数据库更改时重新查询您的游标。

唯一需要注意的是:您必须定义另一个观察者,因为谷歌以其无限的智慧决定将他们的包设为私有。如果您将类放入与原始类(或兼容类)相同的包中,您实际上可以使用原始观察者。观察者是一个非常轻量级的对象,并没有在其他任何地方使用,所以这并没有太大的区别。

于 2012-06-28T17:16:57.717 回答
2

Timo Ohr 提出的第三个选项,连同 Yeung 的评论,提供了最简单的答案(奥卡姆剃刀)。下面是一个适合我的完整类的示例。使用这个类有两个规则。

  1. 扩展这个抽象类并实现getCursor() 和getContentUri() 方法。
  2. 任何时候底层数据库发生变化(例如,在插入或删除之后),确保调用

    getContentResolver().notifyChange(myUri, null);
    

    其中 myUri 与您的方法 getContentUri() 的实现返回的相同。

这是我使用的类的代码:

package com.example.project;

import android.content.Context;
import android.database.Cursor;
import android.content.CursorLoader;
import android.content.Loader;

public abstract class AbstractCustomCursorLoader extends CursorLoader
  {
    private final Loader.ForceLoadContentObserver mObserver = new Loader.ForceLoadContentObserver();

    public AbstractCustomCursorLoader(Context context)
      {
        super(context);
      }

    @Override
    public Cursor loadInBackground()
      {
        Cursor cursor = getCursor();

        if (cursor != null)
          {
            // Ensure the cursor window is filled
            cursor.getCount();
            cursor.registerContentObserver(mObserver);
          }

        cursor.setNotificationUri(getContext().getContentResolver(), getContentUri());
        return cursor;
      }

    protected abstract Cursor getCursor();
    protected abstract Uri getContentUri();
  }
于 2017-03-04T23:20:36.683 回答