Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

POJO's versus Cursors in Android

I usually tend to define the model layer of my apps using POJO's, such as Article, Comment, etc.

I was about to implement an AlphabetIndexer in the adapter of one of my ListViews. Right now this adapter accepts a Collection of Articles, which I normally get from my wrapper around an SQLiteDatabase.

The signature of the AlphabetIndexer constructer is as follows:

public AlphabetIndexer (Cursor cursor, int sortedColumnIndex, CharSequence alphabet)

Since this doesn't accept a Collection or something similar, just a Cursor, it got me wondering: maybe I shouldn't be creating objects for my model, and just use the Cursors returned from the database?

So the question is, I guess: what should I do, represent data with Collections of POJO's, or just work with Cursors throughout my app?

Any input?

like image 278
benvd Avatar asked Mar 30 '10 12:03

benvd


2 Answers

I have run into similar issues. Right now, I am tending away from POJOs. Note, though, that you can create your own Cursor interface for a collection of POJOs, if you so choose.

like image 52
CommonsWare Avatar answered Oct 19 '22 12:10

CommonsWare


I like to create Cursor-backed POJO classes. A Cursor-backed POJO class has a constructor that takes a Cursor and provides the following benefits:

  • Easy to use getters that return the proper content type, much better than getting indexes and having to remember the type of data in the database
  • Getter methods that compute their results from other getters, just like how OO programming ought to be
  • Getter return values can be enums!

These few benefits are well worth some boilerplate code, many many bugs have been averted now that user-engineers aren't accessing cursor columns themselves. We still use the CursorAdapter class, but the first line in the bindView method is to create the Cursor-backed POJO from the Cursor and from then on the code is beautiful.

Below is an example implementation, it's a snap for user-engineers to turn an opaque cursor into clearly defined User object, from that point on it can be passed around and accessed just like a regular POJO so long as the backing cursor is not closed. The SmartUserCursor is a special class I wrote to ensure that the cursor position is remembered and restored before the cursor is accessed and it also stores the cursor column indexes so lookups are fast.

EXAMPLE:

public class User {

    private final SmartUserCursor mCursor;

    public User(SmartUserCursor cursor, int position) {
        mCursor = new SmartUserCursor(cursor, position);
    }

    public long getUserId() {
        return mCursor.getLong(SmartUserCursor.Columns.userId);
    }

    public UserType getType() {
        return UserType.valueOf(mCursor.getString(SmartUserCursor.Columns.type));
    }

    public String getFirstName() {
        return mCursor.getString(SmartUserCursor.Columns.firstName);
    }

    public String getLastName() {
        return mCursor.getString(SmartUserCursor.Columns.lastName);
    }

    public final String getFullName() {
        return getFirstName() + " " + getLastName();
    }

    public static User newUserFromAdapter(BaseAdapter adapter, int position) {
        return new User((SmartUserCursor)adapter.getItem(position), position);
    }

    public static User newUserBlocking(Context context, long UserId) {
        Cursor cursor = context.getContentResolver().query(
                Users.CONTENT_URI_CLIENT,
                Users.DEFAULT_USER_PROJECTION,
                Users.Columns.USER_ID+"=?",
                new String[] {String.valueOf(UserId)},
                null
        );

        if (cursor == null || !cursor.moveToFirst()) {
            throw new RuntimeException("No User with id " + UserId + " exists");
        }

        return new User(new SmartUserCursor(cursor, Users.DEFAULT_USER_PROJECTION), -1);
    }

    public final void closeBackingCursor() {
        mCursor.close();
    }

}
like image 12
satur9nine Avatar answered Oct 19 '22 11:10

satur9nine