POJO's versus Cursors in Android

2019-01-21 11:19发布

问题:

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?

回答1:

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.



回答2:

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();
    }

}


回答3:

One vote for entity objects (POJOs). Passing cursors around, especially to the UI layer, feels so wrong to me (whether or not the Android sdk kinda implies doing it that way). There are usually several ways to populate your UI, and I tend to avoid those that directly use cursors. For example, to populate my custom list views, I use a SimpleAdapter and give my collection objects the ability to return a representation of themselves as a List<? extends Map<String, ?>> for the SimpleAdapter's constructor.

I use a pattern where each table is wrapped by an entity object and has a provider class that handles my CRUD operations associated with that entity. Optionally if I need extended functionality for the collections, I wrap them too (ie. EntityItems extends ArrayList<EntityItem>) The provider has a base class that I pass a reference to a DbAdapter class that does the heavy lifting around the db.

The biggest reason, other than personal preference, is that I want to hide this kind of code as far away from my UI as possible:

String something = cursor.getString(cursor.getColumnIndex(COLUMN_NAME_CONSTANT));

If I see that kind of code inline in the UI layer, I usually expect to see much worse lurking around the corner. Maybe I've just spent too much time in the corporate world working on big teams, but I favor readability unless there's a legit performance concern or if it's a small enough task where the expressiveness is just enterprisey overkill.



回答4:

Answers are 4 years old. I think now we have enought CPU power to get away with more stuff. My idea would be to work only with POJOs and ArrayLists; and extending CursorLoader to map cursor to POJOs in the background and deliver arraylist to activity;

unless youre queries hundreds of rows but then, how often are you doing that vs. niceness of using POJOs, getters and setters