要从数据库中获取数据,我CursorLoader
在应用程序中使用.一旦onLoadFinished()
回调方法调用app的逻辑,就将Cursor
对象转换为List
业务模型需求中的对象.如果有大量数据,那么转换(繁重操作)需要一些时间.这会降低UI线程的速度.我尝试Thread
使用RxJava2
传递Cursor
对象在非UI中启动转换,但得到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 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 loader, Cursor cursor) {
if (cursor.getCount() > 0) {
getFieldsObservable(cursor)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::showFields);
} else {
showNoData();
}
}
private static Observable> getFieldsObservable(Cursor cursor) {
return Observable.defer(() -> Observable.just(getFields(cursor))); <-- Exception raised at this line
}
private static List getFields(Cursor cursor) {
List 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 ArrayList cursorToList(Cursor cursor, Class modelClass) {
ArrayList items = new ArrayList();
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;
}
azizbekian..
6
从我对您的问题的评论中可以看出,我对getFieldsObservable()
尚未返回的数据是否正在更新感兴趣.我收到了您对评论感兴趣的信息.
我可以判断,这是你的情况:
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 {
...
/* 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`
List list = CursorUtil.cursorToList(cursor, Field.class);
return new PojoWrapper(cursor, list);
} finally {
...
}
}
...
}
在哪里PojoWrapper
:
public class PojoWrapper {
Cursor cursor;
List list;
public PojoWrapper(Cursor cursor, List list) {
this.cursor = cursor;
this.list = list;
}
}
因此,onLoadFinished()
您不必将工作委托给另一个线程,因为您已经在Loader
实现中完成了:
@Override public void onLoadFinished(Loader loader, PojoWrapper data) {
List alreadySortedList = data.list;
}
这是整个代码MyCustomLoader
.
1> azizbekian..:
从我对您的问题的评论中可以看出,我对getFieldsObservable()
尚未返回的数据是否正在更新感兴趣.我收到了您对评论感兴趣的信息.
我可以判断,这是你的情况:
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 {
...
/* 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`
List list = CursorUtil.cursorToList(cursor, Field.class);
return new PojoWrapper(cursor, list);
} finally {
...
}
}
...
}
在哪里PojoWrapper
:
public class PojoWrapper {
Cursor cursor;
List list;
public PojoWrapper(Cursor cursor, List list) {
this.cursor = cursor;
this.list = list;
}
}
因此,onLoadFinished()
您不必将工作委托给另一个线程,因为您已经在Loader
实现中完成了:
@Override public void onLoadFinished(Loader loader, PojoWrapper data) {
List alreadySortedList = data.list;
}
这是整个代码MyCustomLoader
.