List-Based Queries
When we have large datasets from the database in our application, we wish to
display them in a ListView, RecyclerView or some other component that recycles
it's views. Instead of running a potentially very large query on the database,
converting it to a List and then keeping that chunk of memory active, we
can lazy-load each row from the query/table.
DBFlow makes it easy using the FlowCursorList, for simple BaseAdapter-like methods,
or the FlowQueryList, which implements the List interface.
Getting one of these lists is as simple as:
FlowQueryList<MyTable> list = SQLite.select()
.from(MyTable.class)
.where(...) // some conditions
.flowQueryList();
FlowCursorList<MyTable> list = SQLite.select()
.from(MyTable.class)
.where(...) // some conditions
.cursorList();
list.close(); // ensure you close these, as they utilize active cursors :)
val list = (select from MyTable::class where (...)).cursorList
val list = (select from MyTable::class where (...)).flowQueryList
list.close()
Any query method allows you to retrieve a default implementation of each. You can also manually instantiate them:
FlowQueryList<MyTable> list = new FlowQueryList.Builder<>(SQLite.select().from(MyTable.class))
.cachingEnabled(false) // caching enabled by default
.build();
FlowCursorList<MyTable> list = new FlowCursorList.Builder<>(SQLite.select().from(MyTable.class))
.cachingEnabled(true)
.modelCache(cache) // provide custom cache for this list
.build();
Caching
Both of these classes come with the ability to cache Model used in it's queries
so that loading only happens once and performance can remain high once loaded. The default
caching mechanism is a ModelLruCache, which provides an LruCache to manage
loading Model.
They are done in almost the same way:
FlowCursorList<MyTable> list = new FlowCursorList.Builder<>(SQLite.select().from(MyTable.class))
.modelCache(cache) // provide custom cache for this list
.build();
FlowQueryList<MyTable> list = new FlowQueryList.Builder<>(SQLite.select().from(MyTable.class))
.modelCache(cache)
.build();
FlowCursorList
The FlowCursorList is simply a wrapper around a standard Cursor, giving it the
ability to cache Model, load items at specific position with conversion, and refresh
it's content easily.
The FlowCursorList by default caches its results, for fast usage. The cache size is determined by the ModelCache you're using. Read on here.
The FlowCursorList provides these methods:
getItem(position)- loads item fromCursorat specified position, caching and loading from cache (if enabled)refresh()- re-queries the underlyingCursor, clears out the cache, and reconstructs it. Use aOnCursorRefreshListenerto get callbacks when this occurs.getAll()- returns aListof all items from theCursor, no caching usedgetCount()- returns count ofCursoror 0 ifCursorisnullisEmpty()- returns if count == 0clearCache()- manually clears cache
Flow Query List
This class is a much more powerful version of the FlowCursorList. It contains a FlowCursorList,
which backs it's retrieval operations.
This class acts as List and can be used almost wherever a List is used. Also, it is a FlowContentObserver
see Observability, meaning other classes can listen
for its specific changes and it can auto-refresh itself when content changes.
Feature rundown:
Listimplementation of a QueryFlowContentObserver, only for the table that it corresponds to in its initialModelQueriablequery statement. Mostly used for self refreshes.- Transact changes to the query asynchronously (note that this refreshes itself every callback unless in a transaction state)
- Caching (almost same implementation as
FlowCursorList)
List Implementation
The List implementation is mostly for convenience. Please note that most of the modification
methods (add, addAll etc.) may not affect the query that you expect it to, unless the object you pass
objects that are valid for the query and you enable self refreshes.
The retrieval methods are where the query works as you would expect. get() calls
getItem() on the internal FlowCursorList, isEmpty(), getCount(), etc all correspond
to the Cursor underneath.
Both FlowQueryList and FlowTableList support Iterator and provide a very
efficient class: FlowCursorIterator that iterates through each row in a Cursor
and provides efficient operations.
Note: any retrieval operation that turns it into another object (i.e. subList(),
toArray, etc) retrieves all objects contained in the query into memory,
and then converts it using the associated method on that returned List.
FlowContentObserver Implementation
Using the FlowContentObserver, we can enable self-refreshes whenever a model changes
for the table this query points to. See Observability.
To turn on self-refreshes, call registerForContentChanges(context), which requeries
the data whenever it changes.
We recommend placing this within a transaction on the FlowQueryList, so we only
refresh content the minimal amount of times:
flowQueryList.beginTransaction();
// perform a bunch of modifications
flowQueryList.endTransactionAndNotify();
To listen for Cursor refreshes register a OnCursorRefreshListener:
modelList
.addOnCursorRefreshListener(new FlowCursorList.OnCursorRefreshListener<ListModel>() {
@Override
public void onCursorRefreshed(FlowCursorList<ListModel> cursorList) {
}
});
Transact Changes Asynchronously
If you want to pass or modify the FlowQueryList asynchronously, set setTransact(true).
This will run all modifications in a Transaction and when completed, a Cursor refresh occurs.
You can also register Transaction.Error and Transaction.Success callbacks for these modifications
on the FlowQueryList to handle when these Transaction finish.