I’m wondering if query() implementation can be improved, by keeping a reference to the Cursor for each Specification, and register content observer/dataset observer in case query results change due to added/removed/updated items (for the case of using SQLite DB)?
Plus looping thru Cursor rows to get list of all items seems to be inefficient, as Cursor is meant more for quick random access to rows? I would imagine returning a list of ‘empty’ objects, and upon being accessed they would be instantiated and populated on demand, using the Cursor that we keep reference to. E.g. query() would return a List wrapper, in which we can override get(position) to move Cursor to that position, and create the object using Cursor data if not already. What do you think?
List<News> query(Specification specification) {
…
Cursor cursor = …;
cursor.registerContentObserver(this);
return new ListWrapper<News>(cursor) {
News fromCursor(Cursor cursor){
// create News object from cursor here
}
};
}// provide some methods to clean up bindings, e.g. close cursors, unregister receivers etc
abstract static class ListWrapper<T> extends List<T> {
private final Cursor cursor;
private final ArrayList<T> innerList;
public ListWrapper(Cursor cursor) {
this.cursor = cursor;
innerList = new ArrayList<>(cursor.getCount());
for (int i = 0; i < cursor.getCount(); i++) {
innerList.add(null);
}
} T get(int index) {
T item;
if (innerList.get(index) == null && cursor.moveToPosition(index)) {
item = fromCursor(cursor);
innerList.set(index, item);
} else {
item = innerList.get(index);
}
if (item == null) {
throw new NotFoundException();
}
return item;
}
// provide logic to create object from cursor
abstract T fromCursor(Cursor cursor);
…
}