Uso de CursorLoader sin ContentProvider

La documentación de Android SDK dice que el método startManagingCursor() está depracated:

Este método está en desuso. Use la nueva clase CursorLoader con LoaderManager; esto también está disponible en plataformas más antiguas a través del paquete de compatibilidad de Android. Este método permite que la actividad se encargue de administrar el ciclo de vida del Cursor dado en función del ciclo de vida de la actividad. Es decir, cuando se detiene la actividad, automáticamente se llamará deactivate () en el Cursor dado, y cuando se reinicie más tarde llamará a requery () por usted. Cuando se destruye la actividad, todos los cursores gestionados se cerrarán automáticamente. Si su objective es HONEYCOMB o posterior, considere usar LoaderManager en su lugar, disponible a través de getLoaderManager ()

Entonces me gustaría usar CursorLoader . ¿Pero cómo puedo usarlo con CursorAdapter personalizado y sin ContentProvider , cuando necesito URI en el constructor de CursorLoader ?

Escribí un CursorLoader simple que no necesita un proveedor de contenido:

 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 { 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. * 

* 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; } }

Solo necesita la clase AsyncTaskLoader . Ya sea el de Android 3.0 o superior, o el que viene con el paquete de compatibilidad.

También escribí un ListLoader que es compatible con LoadManager y se usa para recuperar una colección java.util.List genérica.

Escriba su propio cargador que usa su clase de base de datos en lugar de un proveedor de contenido. La manera más fácil es simplemente tomar la fuente de la clase CursorLoader de la biblioteca de compatibilidad y reemplazar las consultas de los proveedores con consultas a su propia clase de ayuda de db.

SimpleCursorLoader es una solución simple, sin embargo, no es compatible con la actualización del cargador cuando los datos cambian. CommonsWare tiene una biblioteca loaderex que agrega un SQLiteCursorLoader y admite nuevas consultas sobre cambios de datos.

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

Una tercera opción sería simplemente anular 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; } }; 

Esto también se ocupará de volver a consultar el cursor cuando la base de datos cambie.

Solo advertencia: tendrás que definir a otro observador, ya que Google, en su infinita sabiduría, decidió hacer privado el paquete de ellos. Si coloca la clase en el mismo paquete que la original (o la compat), puede usar el observador original. El observador es un objeto muy liviano y no se usa en ningún otro lado, por lo que no hace mucha diferencia.

La tercera opción propuesta por Timo Or, junto con los comentarios de Yeung, proporciona la respuesta más simple (la razor de Occam). A continuación se muestra un ejemplo de una clase completa que funciona para mí. Hay dos reglas para usar esta clase.

  1. Extienda esta clase abstracta e implemente los métodos getCursor () y getContentUri ().
  2. Cada vez que la base de datos subyacente cambie (por ejemplo, después de una inserción o eliminación), asegúrese de llamar

     getContentResolver().notifyChange(myUri, null); 

    donde myUri es el mismo que regresó de su implementación del método getContentUri ().

Aquí está el código para la clase que utilicé:

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