While working with ContentProviders I used a lot of Cursors for passing a data from them. In my use case I didn’t use database Cursors but an instances of cursors created based on data we have stored as POJOs.
To share the data between processes and applications best way in android is using content providers.
There are few Cursor classes that help with creation of the content provider interface:
- MatrixCursor – Allows to create a cursor with set number of Columns and add rows to them with the same sized set of values.
It is very convenient to create a Cursor representation of some array of data or even single item.
MatrixCursor cursor = new MatrixCursor(new String[]{"columnA", "columnB"});
cursor.addRow(new Object[]{"value_A", "value_B"});
- MergeCursor – As name suggest it merges multiply cursors. It can gather many cursors with different schemas (column names and count) and iterate through each row of each cursor. Best way to detect if we switched to new cursor is to check getColumns() count or names of columns. However if you use the MergeCursor for complex POJOs “serialization” you may already know the number of rows and structure of the MergeCursor’s inner cursors.
MatrixCursor cursorA = .....(code to fill cursor).....
MatrixCursor cursorB = .....(code to fill cursor).....
MergeCursor cursor = new MergeCursor(new Cursor[]{cursorA, cursorB});
To make easy serialization/deserialization of POJO to/from Cursor I add pair of methods to the POJO data classes – example:
- toCursor() – converts current class to the cursor – it creates new MatrixCursor or MergeCursor (in case if POJO got array of other types)
- static fromCursor(Cursor cursor) – converts cursor to the POJO class instance, with same order as the cursor was prepared the single class of POJO controls its serialization to/from Cursor.
Everything works great as expected. I can pass the POJO as Cursor in ContentProvider and have it ready to consume in Activity/Service/Fragment/ etc.
My main reason to write this post is the little issue I found out when we put “process” tag into Content Provider manifest XML. When we ask in manifest to run ContentProvider in separate process – it will cut extra columns worth of data inside the MergeCursor, basing only on first cursor row’s columns for the rest of data.
You can see this inside the example application I posted on GITHUB
Below are screenshots of the application, which generates some Data object and displays it in RecyclerView
Example app demo:

Notice “-1” value is coming from column size check of DataItem class.
When digging through this “bug” I found out that there CursorWindow is used for passing the data between processes.I checked v24 sources for AbstractCursor – which one’s implementation of method `public CursorWindow getWindow()`, basically new CursorWindow is created and data is copied to it with use of DatabaseUtil class method:
/**
* Fills the specified cursor window by iterating over the contents of the cursor.
* The window is filled until the cursor is exhausted or the window runs out
* of space.
*
* The original position of the cursor is left unchanged by this operation.
*
* @param cursor The cursor that contains the data to put in the window.
* @param position The start position for filling the window.
* @param window The window to fill.
* @hide
*/
public static void cursorFillWindow(final Cursor cursor,
int position, final CursorWindow window)
That method doesn’t take into account the change of columns per each next ‘row’ of CursorWindow (which in MergeCursor may be next Cursor that was merged) in size of cursors columns while iterating through them.
What to do now? Write a overload getWindow() method for MergeCursor that will do it as expected or fix the DatabaseUtils cursorFillWIndow(...) method? I think better is the overload getWindow() method and use custom MergeCursor.
Maybe use of MergeCursor should be better documented and mark that Cursors of different column count should not be supported in separate Process ContentProviders?
I didn’t found any bug about this issue. Time to file a bug with Android Framework.I didn’t found any bug about this issue..