为了从数据库中获取数据,我在应用程序中使用了CursorLoader。一旦onLoadFinished()回调方法调用,应用程序的逻辑就会在业务模型要求内将Cursor对象转换为对象的List。如果有大量数据,则该转换(繁重的操作)将花费一些时间。这会减慢UI线程的速度。我试图通过传递Thread对象的RxJava2在非UI的Cursor中开始转换,但得到了Exception:

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

这是Fragment的代码的一部分:
@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;
        }

此处使用CursorLoader的目的是,如果数据存储已更新,则可以从DB获取通知。

更新
正如Tin Tran所建议的那样,我删除了CursorUtil.closeSafely(cursor);,现在又得到另一个异常:
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() 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;
    }

最佳答案

my comment到您的问题,您可以看到,我很感兴趣是否在尚未返回getFieldsObservable()的情况下更新数据。我收到了我感兴趣的信息in your comment

据我判断,这是您的情况:

  • onLoadFinished() 与Cursor-1一起调用
  • RxJava的方法正在使用Cursor-1的另一个线程上执行(尚未完成,此处正在使用Cursor-1)
  • onLoadFinished() 是用Cursor-2调用的,LoaderManager API负责关闭Cursor-1,RxJava仍在另一个线程
  • 上查询它

    因此,导致异常。

    因此,您最好坚持创建自定义 AsyncTaskLoader ( CursorLoader 扩展自定义)。此 AsyncTaskLoader 将合并 CursorLoader 具有的所有逻辑(基本上是一对一的副本),但是会在onLoadFinished(YourCustomObject)中返回已排序/过滤的对象。因此,您希望使用RxJava执行的操作实际上是由加载程序的 loadInBackground() 方法完成的。

    这是MyCustomLoader将在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 {
          ...
        }
      }
      ...
    }
    

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

    因此,在 onLoadFinished() 中,您不必将工作委派给另一个线程,因为您已经在 Loader 实现中完成了它:
    @Override public void onLoadFinished(Loader<PojoWrapper> loader, PojoWrapper data) {
          List<Pojo> alreadySortedList = data.list;
    }
    

    Here's整个MyCustomLoader的代码。

    关于android - CursorLoader的onLoadFinished回调中的RxJava2,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/44390030/

    10-11 22:15