oncreateloader example custom android cursor illegalstateexception android-loadermanager

example - Error de Android: java.lang.IllegalStateException: intentando volver a consultar un cursor ya cerrado



loader android example (6)

entorno (Linux / Eclipse Dev para Xoom Tablet con HoneyComb 3.0.1)

En mi aplicación, estoy usando la cámara (startIntentForResult ()) para tomar una foto. Después de tomar la foto, obtengo la devolución de llamada onActivityResult () y puedo cargar un mapa de bits utilizando un Uri pasado a través de la intención de "tomar foto". En ese momento, mi actividad se reanuda y aparece un error al intentar volver a cargar las imágenes en una galería:

FATAL EXCEPTION: main ERROR/AndroidRuntime(4148): java.lang.RuntimeException: Unable to resume activity {...}: java.lang.IllegalStateException: trying to requery an already closed cursor at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2243) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1019) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:126) at android.app.ActivityThread.main(ActivityThread.java:3997) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:491) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599) at dalvik.system.NativeStart.main(Native Method) Caused by: java.lang.IllegalStateException: trying to requery an already closed cursor at android.app.Activity.performRestart(Activity.java:4337) at android.app.Activity.performResume(Activity.java:4360) at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2205) ... 10 more

La única lógica de cursor que estoy usando es que después de tomar la imagen, convierto el Uri en un archivo usando la siguiente lógica

String [] projection = { MediaStore.Images.Media._ID, MediaStore.Images.ImageColumns.ORIENTATION, MediaStore.Images.Media.DATA }; Cursor cursor = activity.managedQuery( uri, projection, // Which columns to return null, // WHERE clause; which rows to return (all rows) null, // WHERE clause selection arguments (none) null); // Order-by clause (ascending by name) int fileColumnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); if (cursor.moveToFirst()) { return new File(cursor.getString(fileColumnIndex)); } return null;

¿Alguna idea de lo que estoy haciendo mal?


Este problema me acosó durante mucho tiempo y finalmente se me ocurrió una solución simple que funciona como un encanto en todas las versiones de Android. Primero, no use startManagingCursor () ya que, evidentemente, tiene errores y está obsoleto en cualquier caso. En segundo lugar, cierre un Cursor tan pronto como sea posible después de que haya terminado con él. Utilizo el intento y finalmente para asegurar que el cursor se cierre en todas las circunstancias. Si su método debe devolver un Cursor, entonces la rutina de llamada es responsable de cerrarlo lo antes posible.

Solía ​​dejar los cursores abiertos durante toda la vida de una actividad, pero desde entonces la he abandonado para este enfoque transaccional. Ahora mi aplicación es muy estable y no sufre de "Error de Android: java.lang.IllegalStateException: tratando de volver a consultar un cursor ya cerrado" al cambiar de Actividades aunque tengan acceso a la misma base de datos.

static public Boolean musicReferencedByOtherFlash(NotesDB db, long rowIdImage) { Cursor dt = null; try { dt = db.getNotesWithMusic(rowIdImage); if ( (dt != null) && (dt.getCount() > 1)) return true; } finally { if (dt != null) dt.close(); } return false; }


FIX: use context.getContentResolver().query lugar de activity.managedQuery .

Cursor cursor = null; try { cursor = context.getContentResolver().query(uri, PROJECTION, null, null, null); } catch(Exception e) { e.printStackTrace(); } return cursor;


He creado esta pregunta aquí porque no pude comentar sobre la última respuesta (comentarios deshabilitados por alguna razón). Pensé que abrir un nuevo hilo en esto solo complicaría las cosas.

Tengo errores de aplicación cuando paso de la Actividad A a la Actividad B y luego vuelvo a la Actividad A. Esto no sucede todo el tiempo, solo algunas veces y me cuesta trabajo encontrar EXACTAMENTE dónde ocurre esto. Todo sucede en el mismo dispositivo (Nexus S), pero no creo que esto sea un problema de dispositivo.

Tengo algunas preguntas con respecto a la respuesta de @Martin Stine.

  • En la documentación dice sobre changeCursor(c); : "Cambie el cursor subyacente a un nuevo cursor. Si hay un cursor existente, se cerrará". Entonces, ¿por qué tengo que dejar de stopManagingCursor(currentCursor); - ¿No es eso redundante ?
  • Cuando uso el código ofrecido por @Martin Stine, obtengo una excepción de puntero nulo. El motivo es que en la primera "ejecución" de la aplicación ((SimpleCursorAdapter)getListAdapter()) se evaluará en NULL porque aún no se ha creado ningún cursor. Claro, puedo comprobar si NO obtengo un nulo y solo entonces intento parar para manejar el cursor, pero finalmente decidí colocar mi `stopManagingCursor (currentCursor); en el método onPause () de esta actividad. Pensé de esta manera, seguro que tendré un cursor para dejar de administrar y debería hacerlo justo antes de dejar la Actividad en una diferente. El problema: estoy usando varios cursores (uno para rellenar un texto de un campo EditarTexto y el otro para una vista de lista) en mi actividad, creo que no todos están relacionados con un cursor ListAdapter.
    • ¿Cómo puedo saber cuál dejar de gestionar? Si tengo 3 vistas de lista diferentes?
    • ¿Debo cerrarlas todas en onPause() ?
    • ¿Cómo obtengo una lista de todos mis cursores abiertos?

Tantas preguntas ... Espero que alguien pueda ayudar.

Cuando llego a onPause() tengo un cursor para dejar de administrar, pero todavía tengo que determinar si esto resuelve el problema, ya que este error aparece esporádicamente.

¡Muchas gracias!

DESPUÉS DE ALGUNAS INVESTIGACIONES:

Encontré algo interesante que podría dar la respuesta al lado "misterioso" de este problema:

La actividad A utiliza dos cursores: uno para rellenar un campo EditarTexto. El otro es rellenar un ListView.

Al pasar de la Actividad A a la Actividad B y regresar, el campo + ListView en la Actividad A debe completarse nuevamente. Parece que el campo EditText nunca tendrá un problema con eso. No pude encontrar una manera de obtener el cursor actual del campo EditText (como en Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor(); ) y la razón me dice que el campo EditText no lo mantendrá. Por otro lado, el ListView "recordará" su cursor desde la última vez (desde antes de la Actividad A -> Actividad B). Además, y esto es lo extraño, el Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor(); tendrá una ID diferente después de la Actividad B -> Actividad A y todo esto SIN llamarme

Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor(); stopManagingCursor(currentCursor);

Supongo que en algunos casos, cuando el sistema necesita liberar recursos, el cursor se matará y cuando la Actividad B -> Actividad A, el sistema intentará usar este antiguo cursor muerto, lo que resultará en una Excepción. Y en otros casos, el sistema creará un nuevo cursor que aún está vivo y, por lo tanto, no se producirá una excepción. Esto podría explicar por qué esto aparece solo a veces. Supongo que esto es difícil de depurar debido a la diferencia de velocidad de la aplicación cuando se ejecuta O la depuración de la aplicación. Cuando se realiza la depuración, se necesita más tiempo y, por lo tanto, puede darle tiempo al sistema para crear un nuevo cursor o viceversa.

En mi entendimiento esto hace que el uso de

Cursor currentCursor = ((SimpleCursorAdapter)currentListAdapter).getCursor(); stopManagingCursor(currentCursor);

Según lo recomendado por @Martin Stine, debe en algunos casos y es redundante en OTROS: si vuelvo al método y el sistema está tratando de usar un cursor muerto, uno debe crear un nuevo cursor y reemplazarlo en el adaptador de lista, de lo contrario, me enojo. usuarios de la aplicación con una aplicación se estrelló. En otro caso, cuando el sistema se encuentra con un nuevo cursor, las líneas anteriores son redundantes, ya que invalidan un buen cursor y crean uno nuevo.

Supongo que para evitar esta redundancia necesitaría algo como esto:

ListAdapter currentListAdapter = getListAdapter(); Cursor currentCursor = null; Cursor c = null; //prevent Exception in case the ListAdapter doesn''t exist yet if(currentListAdapter != null) { currentCursor = ((SimpleCursorAdapter)currentListAdapter).getCursor(); //make sure cursor is really dead to prevent redundancy if(currentCursor != null) { stopManagingCursor(currentCursor); c = db.fetchItems(selectedDate); ((SimpleCursorAdapter)getListAdapter()).changeCursor(c); } else { c = db.fetchItems(selectedDate); } } else { c = db.fetchItems(selectedDate); } startManagingCursor(c);

Me encantaría escuchar lo que piensas sobre esto!


Para el registro, he aquí cómo solucioné esto en mi código (que se ejecuta en Android 1.6 y versiones posteriores): el problema en mi caso era que estaba cerrando inadvertidamente los cursores administrados llamando a CursorAdapter.changeCursor (). Llamar a Activity.stopManagingCursor () en el cursor del adaptador antes de cambiar el cursor resolvió el problema:

// changeCursor() will close current one for us: we must stop managing it first. Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor(); // *** adding these lines stopManagingCursor(currentCursor); // *** solved the problem Cursor c = db.fetchItems(selectedDate); startManagingCursor(c); ((SimpleCursorAdapter)getListAdapter()).changeCursor(c);


Parece que la llamada managedQuery () está en desuso en la API de Honeycomb.

Doc para managedQuery () lee:

This method is deprecated. Use CursorLoader instead. Wrapper around query(android.net.Uri, String[], String, String[], String) that the resulting Cursor to call startManagingCursor(Cursor) so that the activity will manage its lifecycle for you. **If you are targeting HONEYCOMB or later, consider instead using LoaderManager instead, available via getLoaderManager()**.

También noté que estaba llamando a cursor.close () después de la consulta, que supongo que es un no-no. Encontré este enlace realmente útil también. Después de una lectura, se me ocurrió este cambio que parece funcionar.

// causes problem with the cursor in Honeycomb Cursor cursor = activity.managedQuery( uri, projection, // Which columns to return null, // WHERE clause; which rows to return (all rows) null, // WHERE clause selection arguments (none) null); // Order-by clause (ascending by name) // ------------------------------------------------------------------- // works in Honeycomb String selection = null; String[] selectionArgs = null; String sortOrder = null; CursorLoader cursorLoader = new CursorLoader( activity, uri, projection, selection, selectionArgs, sortOrder); Cursor cursor = cursorLoader.loadInBackground();


Solo agrega el siguiente código al final del bloque del cursor.

try { Cursor c = db.displayName(number); startManagingCursor(c); if (!c.moveToFirst()) { if (logname == null) logname = "Unknown"; System.out.println("Null " + logname); } else { logname = c.getString(c .getColumnIndex(DataBaseHandler.KEY_NAME)); logdp = c.getBlob(c .getColumnIndex(DataBaseHandler.KEY_IMAGE)); // tvphoneno_oncall.setText(logname); System.out.println("Move name " + logname); System.out.println("Move number " + number); System.out.println("Move dp " + logdp); } stopManagingCursor(c); }