Join FlipAndroid.COM Telegram Group: https://t.me/joinchat/F_aqThGkhwcLzmI49vKAiw


RxJava2 en la función de devolución de llamada onLoadFinished de CursorLoader

Para obtener datos de la base de datos uso CursorLoader en la aplicación. Una vez que el método de devolución de llamada onLoadFinished() llama a la lógica de la aplicación convierte el objeto Cursor en List de objetos dentro de los requisitos del modelo de negocio. Esa conversión (operación pesada) lleva algún tiempo si hay una gran cantidad de datos. Que se ralentiza el hilo de la interfaz de usuario. Intenté comenzar la conversión en el Thread no-UI usando RxJava2 pasa el objeto del Cursor , pero conseguido Exception :

 Caused by: android.database.StaleDataException: Attempting to access a closed CursorWindow.Most probable cause: cursor is deactivated prior to calling this method. 

Aquí está la parte del código de Fragment :

  • AlphabetIndexer con adaptador personalizado administrado por LoaderManager
  • Android 3.0 - ¿Cuáles son las ventajas de usar instancias de LoaderManager exactamente?
  • ¿Cómo CursorLoader actualiza automáticamente la vista incluso si la aplicación está inactiva?
  • ¿Cuál es la ventaja de los cargadores sobre Asynctask en Android?
  • El uso de CursorLoader para obtener correos electrónicos provoca la duplicación de correos electrónicos
  • Prueba de ViewPager (y CursorLoader) con Robolectric
  •  @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { QueryBuilder builder; switch (id) { case Constants.FIELDS_QUERY_TOKEN: builder = QueryBuilderFacade.getFieldsQB(activity); return new QueryCursorLoader(activity, builder); default: return null; } } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { if (cursor.getCount() > 0) { getFieldsObservable(cursor) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(this::showFields); } else { showNoData(); } } private static Observable<List<Field>> getFieldsObservable(Cursor cursor) { return Observable.defer(() -> Observable.just(getFields(cursor))); <-- Exception raised at this line } private static List<Field> getFields(Cursor cursor) { List<Field> farmList = CursorUtil.cursorToList(cursor, Field.class); CursorUtil.closeSafely(cursor); return farmList; } 

    El propósito de utilizar CursorLoader aquí es obtener notificaciones de DB si hay un almacén de datos actualizado.

    Actualización Como sugerido Tin Tran, he eliminado CursorUtil.closeSafely(cursor); Y ahora tengo otra excepción:

     Caused by: java.lang.IllegalStateException: attempt to re-open an already-closed object: /data/user/0/com.my.project/databases/db_file at android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:55) at android.database.CursorWindow.getNumRows(CursorWindow.java:225) at android.database.sqlite.SQLiteCursor.onMove(SQLiteCursor.java:121) at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:236) at android.database.AbstractCursor.moveToNext(AbstractCursor.java:274) at android.database.CursorWrapper.moveToNext(CursorWrapper.java:202) at com.db.util.CursorUtil.cursorToList(CursorUtil.java:44) at com.my.project.MyFragment.getFields(MyFragment.java:230) 

    cursorToList() método de CursorUtil

     public static <T> ArrayList<T> cursorToList(Cursor cursor, Class<T> modelClass) { ArrayList<T> items = new ArrayList<T>(); if (!isCursorEmpty(cursor)) { while (cursor.moveToNext()) { <-- at this line (44) of the method raised that issue final T model = buildModel(modelClass, cursor); items.add(model); } } return items; } 

  • Cómo cambiar programatically Edittext Cursor Color en android?
  • Cómo poner un tarro en classpath en Eclipse?
  • ExpandableListView colapsar todos los elementos primarios
  • Cómo encontrar y borrar el archivo db de SQLite en Android (emulador)
  • ¿Cómo puedo añadir un TextView a un LinearLayout dinámicamente en Android?
  • Cómo configurar proguard para eliminar SOLAMENTE llamadas de registro de android
  • 2 Solutions collect form web for “RxJava2 en la función de devolución de llamada onLoadFinished de CursorLoader”

    Como puedes ver en mi comentario a tu pregunta, me interesó saber si los datos se están actualizando mientras getFieldsObservable() no ha sido devuelto. Recibí la información que me interesó en su comentario .

    Como puedo juzgar, esto es lo que sucede en su caso:

    • onLoadFinished() se llama con Cursor-1
    • El método de RxJava se está ejecutando en otro hilo con Cursor-1 (todavía no se ha terminado, aquí se está usando Cursor-1)
    • onLoadFinished() se llama con Cursor-2, LoaderManager API se encarga de cerrar Cursor-1 , que todavía está siendo consultado por RxJava en otro hilo

    Por lo tanto, se produce una excepción.

    Por lo tanto, es mejor seguir con la creación de su personalizado AsyncTaskLoader (que CursorLoader extiende desde). Este AsyncTaskLoader incorporará todas las lógicas que CursorLoader tiene (básicamente una copia uno a uno), pero devolverá el objeto que ya está ordenado / filtrado en onLoadFinished(YourCustomObject) . Por lo tanto, la operación, que usted desea realizar utilizando RxJava realmente sería hecho por su cargador en su método loadInBackground() .

    Esta es la instantánea de los cambios que MyCustomLoader tendrá en el método loadInBackground() :

     public class MyCustomLoader extends AsyncTaskLoader<PojoWrapper> { ... /* Runs on a worker thread */ @Override public PojoWrapper loadInBackground() { ... try { Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection, mSelectionArgs, mSortOrder, mCancellationSignal); ... // `CursorLoader` performs following: // return cursor; // We perform some operation here with `cursor` // and return PojoWrapper, that consists of `cursor` and `List<Pojo>` List<Pojo> list = CursorUtil.cursorToList(cursor, Field.class); return new PojoWrapper(cursor, list); } finally { ... } } ... } 

    Donde PojoWrapper es:

     public class PojoWrapper { Cursor cursor; List<Pojo> list; public PojoWrapper(Cursor cursor, List<Pojo> list) { this.cursor = cursor; this.list = list; } } 

    Por lo tanto, en onLoadFinished() no tiene que encargarse de delegar el trabajo a otro hilo, ya que ya lo ha hecho en la implementación de Loader :

     @Override public void onLoadFinished(Loader<PojoWrapper> loader, PojoWrapper data) { List<Pojo> alreadySortedList = data.list; } 

    Aquí está el código completo de MyCustomLoader .

    El cargador liberará los datos una vez que sepa que la aplicación ya no lo está utilizando. Por ejemplo, si los datos son un cursor de un CursorLoader, no debe llamar a close () en él mismo. De: https://developer.android.com/guide/components/loaders.html

    Usted no debe cerrar el cursor usted mismo que creo CursorUtil.closeSafely(cursor) hace.

    Puede utilizar el operador switchMap para implementarlo. Hace exactamente lo que queremos

     private PublishSubject<Cursor> cursorSubject = PublishSubject.create() public void onCreate(Bundle savedInstanceState) { cursorSubject .switchMap(new Func1<Cursor, Observable<List<Field>>>() { @Override public Observable<List<Field>> call(Cursor cursor) { return getFieldsObservable(cursor); } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(this::showFields); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { cursorSubject.onNext(cursor) } 

    Ahora tiene que modificar showFields y getFieldsObservable para tener en cuenta el Cursor vacío

    FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.