¿Cómo CursorLoader actualiza automáticamente la vista, incluso si la aplicación está inactiva?

He estado trabajando en una pequeña aplicación de listas de cosas por hacer. Utilicé CursorLoader para actualizar ToDolistview de un proveedor de contenido. Tengo una función escrita onNewItemAdded() , que se onNewItemAdded() cuando el usuario ingresa un nuevo elemento en la vista de texto y hace clic en ingresar. Consulte a continuación:

 public void onNewItemAdded(String newItem) { ContentResolver cr = getContentResolver(); ContentValues values = new ContentValues(); values.put(ToDoContentProvider.KEY_TASK, newItem); cr.insert(ToDoContentProvider.CONTENT_URI, values); // getLoaderManager().restartLoader(0, null, this); // commented for the sake of testing } @Override protected void onResume() { super.onResume(); //getLoaderManager().restartLoader(0, null, this); // commented for the sake of testing } public Loader onCreateLoader(int id, Bundle args) { CursorLoader loader = new CursorLoader(this, ToDoContentProvider.CONTENT_URI, null, null, null, null); Log.e("GOPAL", "In the onCreateLoader"); return loader; } public void onLoadFinished(Loader loader, Cursor cursor) { int keyTaskIndex = cursor.getColumnIndexOrThrow(ToDoContentProvider.KEY_TASK); Log.e("GOPAL", "In the onLoadFinished"); todoItems.clear(); if (cursor.moveToNext() == false) Log.e("GOPAL", "Empty Cursor"); else { while (cursor.moveToNext()) { ToDoItem newItem = new ToDoItem(cursor.getString(keyTaskIndex)); todoItems.add(newItem); } aa.notifyDataSetChanged(); // aa is arrayadapter used for the listview } } 

He leído, CursorLoader actualiza automáticamente la vista, siempre que haya un cambio de datos en el proveedor de contenido db. Eso significa que supongo que getLoaderManager().restartLoader(0, null, this) debe getLoaderManager().restartLoader(0, null, this) implícitamente cada vez que haya un cambio en los datos, ¿no? Pero eso no está sucediendo. Cada vez que agrego un nuevo elemento (el elemento se agrega a la base de onNewItemAdded desde onNewItemAdded , pero no se llama explícitamente a restartLoader), pause esta actividad y reiníciela. No veo ninguna llamada implícita a restartLoader (incluso si se cambió db) y la vista de lista tampoco se actualiza con el nuevo elemento agregado. ¿Porqué es eso? ¿Cómo actualiza CursorLoader automáticamente la vista incluso si la aplicación no está activa? Gracias 🙂

EDITAR: También he usado getContext().getContentResolver().notifyChange(insertedId, null) en la inserción de mi proveedor de contenido.

Encontré la respuesta para mi pregunta. En general, CursorLoader no detecta automáticamente los cambios de datos y los carga para verlos. Necesitamos rastrear URI para cambios. Esto se puede hacer siguiendo los siguientes pasos:

  1. Registrar un observador en la resolución de contenido a través del cursor usando: (Hecho en el método de consulta de ContentProvider)
    cursor.setNotificationUri(getContext().getContentResolver(), uri);

  2. Ahora cuando hay algún cambio en los datos subyacentes de URI usando insert() / delete() / update(), notificamos a ContentResolver sobre el cambio usando:

    getContext().getContentResolver().notifyChange(insertedId, null);

  3. Esto es recibido por el observador, lo registramos en el paso 1 y esto llama a ContentResolver.query() , que a su vez llama al método query() ContentProvider para devolver un cursor nuevo a LoaderManager . LoaderManager llama a onLoadFinished() pasando este cursor, junto con CursorLoader donde actualizamos la Vista (usando Adapter.swapCursor() ) con datos nuevos.

Para Custom AsyncTaskLoaders:

A veces necesitamos nuestro cargador personalizado en lugar de CursorLoader. Aquí podemos usar algún otro objeto que no sea el cursor para señalar los datos cargados (como la lista, etc.). En esto no tendremos previlige para notificar a ContentResolver a través del cursor. La aplicación también puede no tener un Proveedor de contenido, para rastrear los cambios de URI. En este escenario, usamos BroadcastReceiver o ContentObserver explícito para lograr la actualización de vista automática. Esto es como sigue:

  1. Necesitamos definir nuestro cargador personalizado que extienda AsyncTaskLoader e implemente todos sus métodos abstractos. A diferencia de CursorLoader , nuestro cargador personalizado puede o no usar un proveedor de contenido y su constructor no puede llamar a ContentResolver.query() , cuando este cargador está instalado. Entonces usamos un receptor de difusión para cumplir el propósito.
  2. Necesitamos instanciar un BroadCastReceiver o ContentObserver en el método OnStartLoading() de la clase abstracta AsyncTaskLoader .
  3. Este receptor BroadCast debe definirse para recibir transmisiones que cambian los datos del proveedor de contenido o cualquier evento del sistema (como la nueva aplicación instalada) y debe llamar al método onContentChanged() del cargador para notificar al cargador sobre el cambio de datos. Loader automáticamente hace el rest para cargar los datos actualizados y llama a onLoadFinished() para actualizar la vista.

Para obtener más detalles, consulte esto: http://developer.android.com/reference/android/content/AsyncTaskLoader.html

Encontré esto muy útil para una explicación clara: http://www.androiddesignpatterns.com/2012/08/implementing-loaders.html

Bueno, creo que puedes reiniciar el cargador en ciertos eventos. Por ejemplo, en mi caso tengo una actividad de TODOs. Al hacer clic en la opción ‘agregar’, se inicia una nueva actividad que tiene vista para alimentar a TODO nuevo.

Estoy usando el siguiente código en la actividad principal de onActivityResult ()

 getLoaderManager().restartLoader(0, null, this); 

Funciona bien para mí. Por favor, comparta si hay un mejor enfoque.

obtener una referencia a su cargador al inicializar de la siguiente manera

  Loader dayWeatherLoader = getLoaderManager().initLoader(LOADER_DAY_WEATHER, null, this); 

luego crea una clase que extienda ContentObserver de la siguiente manera

  class DataObserver extends ContentObserver { public DataObserver(Handler handler) { super(handler); } @Override public void onChange(boolean selfChange, Uri uri) { dayWeatherLoader.forceLoad(); } } 

A continuación, registre el observador de contenido dentro del método de ciclo de vida Reanudar de la siguiente manera

 @Override public void onResume() { super.onResume(); getContext().getContentResolver().registerContentObserver(CONTENTPROVIDERURI,true,new DayWeatherDataObserver(new Handler())); } 

Siempre que haya un cambio en los datos subyacentes del proveedor de contenido, se llamará al método onChange de contentobserver, donde puede pedirle al cargador que cargue los datos nuevamente.