android cursor utils - nyc android meetup
DESCRIPTION
An overview of Venmo's Cursor Utilities library given at the New York Android Meetup. The library simplifies turning SQLite cursors into Java Objects, using large relational data in ListViews, and iterating through SQL queriesTRANSCRIPT
Cursor Utils
CURSORS:THE GOOD PARTS
▸ Fast▸ Flexible
▸ Good for large datasets
CURSORS:THE ROUGH POINTS
▸ No type safety▸ Lots of code duplication▸ No easy iteration mechanism
Cursor UtilsTO THE RESCUE!
IterableCursor<T>extends Iterable<T>, Cursor
// Public API:
public T peek();public T nextDocument();public T previousDocument();public Iterator<T> iterator();
IterableCursor<T>extends Iterable<T>, Cursor
// Public API:
public T peek();// public T nextDocument();// public T previousDocument(); // @see CursorUtils.java helpers// public Iterator<T> iterator();
That's it.
public class User { public User(String name, String bio) { /* ... */ }}
public class UserCursor extends IterableCursorWrapper<User> {
public UserCursor(Cursor c) { super(c); }
public User peek() { String name = getStringHelper(COLUMN_USER_NAME, "Default Name"); String bio = getLongHelper(COLUMN_USER_BIO, "No bio yet"); return new User(name, bio); }
}
IterableCursorWrapper<T> Helper Methods
getString(columnIndex) vs. getStringHelper(columnName, defaultValue)getDouble(columnIndex) vs. getDoubleHelper(columnName, defaultValue)// ...get*(columnIndex) vs. get*Helper(columnName, defaultValue)
IterableCursorWrapper<T> Helper Methods
getString(columnIndex) vs. getStringHelper(columnName, defaultValue)getDouble(columnIndex) vs. getDoubleHelper(columnName, defaultValue)// ...get*(columnIndex) vs. get*Helper(columnName, defaultValue)
Effective Java, Item 79: Use these helper methods judiciously.
IterableCursorWrapper<T> Helper Methods
getString(columnIndex) vs. getStringHelper(columnName, defaultValue)getDouble(columnIndex) vs. getDoubleHelper(columnName, defaultValue)// ...get*(columnIndex) vs. get*Helper(columnName, defaultValue)
Effective Java, Item 79: Use these helper methods judiciously.They're convenient, but definitely slower.
Back to code
public class MyDatabase extends SQLiteOpenHelper {
// ...
public IterableCursor<User> queryAllUsers() { Cursor cursor = getReadableDatabase().query(TABLE_USERS, /* ... */); return new UserCursor(cursor); }
}
IterableCursor<User> users = myDb.queryAllUsers();for (User user : users) { if (user.isMyBestFriend()) giveSomeCake(user);}users.close();
IterableCursorAdapter<T>Here's where the magic happens
IterableCursorAdapter<T>
public abstract View newView(Context context, T t, ViewGroup parent);public abstract void bindView(View view, Context context, T t);
IterableCursorAdapter<T>
public abstract View newView(Context context, T t, ViewGroup parent);public abstract void bindView(View view, Context context, T t);
"It's like ArrayAdapter<T> + CursorAdapter!"
— Our Intern
CursorListIterableCursor<T> + List<T>
CursorListIterableCursor<T> + List<T>(Cursor + Iterable<T>) + List<T>
CursorList
Useful if you want to only have one instance created per row.
(IterableCursorWrapper<T> by default creates a new instance on every .peek())
CursorList
Great for filtering data sets:
@Overridepublic IterableCursor<User> runQueryOnBackgroundThread(CharSequence constraint) { CursorList<User> filtered = new CursorList<User>(); for (User user : getCursor()) { if (user.nameMatches(constraint)) { filtered.add(user); } } return filtered;}
For the RxJava Folks:
Observable<IterableCursor<Pojo>> observable = // ...observable .map(cursor -> new CursorList(cursor)) .subscribeOn(Schedulers.io) .observeOn(AndroidSchedulers.mainThread())
For the RxJava Folks:
Observable<IterableCursor<Pojo>> pojos = // ... Observable.from(pojos) .forEach(pojo -> /* ... */);
github.com/venmo/cursor-utils compile ‘com.venmo.cursor:library:0.2’
!
Contribute!
Where can I get the new hotness?