为了从数据库中获取数据,我在应用程序中使用了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一起调用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/