Использование CursorLoader без ContentProvider - PullRequest
107 голосов
/ 25 августа 2011

Документация Android SDK гласит, что startManagingCursor() метод устарел:

Этот метод устарел. Вместо этого используйте новый класс CursorLoader с LoaderManager; это также доступно на старых платформах через пакет совместимости Android. Этот метод позволяет заданию позаботиться об управлении жизненным циклом данного Курсора для вас на основе жизненного цикла задания. То есть, когда действие останавливается, оно автоматически вызывает deactivate () для данного Курсора, а когда оно будет перезапущено позднее, оно будет вызывать Requery () для вас. Когда действие уничтожено, все управляемые курсоры будут закрыты автоматически. Если вы нацелены на HONEYCOMB или более позднюю версию, рассмотрите вместо этого использование LoaderManager вместо этого, доступного через getLoaderManager ()

Так что я бы хотел использовать CursorLoader. Но как я могу использовать его с пользовательским CursorAdapter и без ContentProvider, когда мне нужен URI в конструкторе CursorLoader?

Ответы [ 5 ]

154 голосов
/ 15 сентября 2011

Я написал простой 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.

23 голосов
/ 25 августа 2011

Напишите свой собственный загрузчик, который использует ваш класс базы данных вместо поставщика контента.Самый простой способ - просто взять источник класса CursorLoader из библиотеки совместимости и заменить запросы провайдеров запросами на свой собственный вспомогательный класс db.

14 голосов
/ 22 июня 2012

SimpleCursorLoader - это простое решение, однако оно не поддерживает обновление загрузчика при изменении данных. CommonsWare имеет библиотеку loaderex, которая добавляет SQLiteCursorLoader и поддерживает повторный запрос на изменения данных.

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

12 голосов
/ 28 июня 2012

Третий вариант - просто переопределить 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;
    }
};

Это также позаботится о повторном запросе курсора при изменении базы данных.

Только предостережение: вам придется определить другого наблюдателя, так как Google в своей бесконечной мудрости решил сделать их пакет приватным. Если вы поместите класс в тот же пакет, что и исходный (или совместимый), вы на самом деле можете использовать исходный наблюдатель. Наблюдатель является очень легким объектом и больше нигде не используется, так что это не имеет большого значения.

2 голосов
/ 05 марта 2017

Третий вариант, предложенный Тимо Ором, вместе с комментариями Йенга дают самый простой ответ (бритва Оккама). Ниже приведен пример полного класса, который работает для меня. Есть два правила использования этого класса.

  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();
  }
...