Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android ContentProvider URI scheme to notify CursorAdapters listening on OUTER JOIN queries

I have an Android ContentProvider which allows to do LEFT OUTER JOIN queries on a SQLite database.

Let's assume in the database I have 3 tables, Users, Articles and Comments. The ContentProvider is something like the following:

public class SampleContentProvider extends ContentProvider {
    private static final UriMatcher sUriMatcher;
    public static final String AUTHORITY = "com.sample.contentprovider";
    private static final int USERS_TABLE = 1;
    private static final int USERS_TABLE_ID = 2;
    private static final int ARTICLES_TABLE = 3;
    private static final int ARTICLES_TABLE_ID = 4;
    private static final int COMMENTS_TABLE = 5;
    private static final int COMMENTS_TABLE_ID = 6;
    private static final int ARTICLES_USERS_JOIN_TABLE = 7;
    private static final int COMMENTS_USERS_JOIN_TABLE = 8;

    // [...] other ContentProvider methods

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        String table = getTableName(uri);

        // SQLiteWrapper is a wrapper class to manage a SQLiteHelper
        Cursor c = SQLiteWrapper.get(getContext()).getHelper().getReadableDatabase()
                .query(table, projection, selection, selectionArgs, null, null, sortOrder);

        c.setNotificationUri(getContext().getContentResolver(), uri);
        return c;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        String table = getTableName(uri);

        // SQLiteWrapper is a wrapper class to manage a SQLiteHelper
        long id = SQLiteWrapper.get(getContext()).getHelper().getWritableDatabase()
                .insert(table, null, values);

        Uri itemUri = ContentUris.withAppendedId(uri, id);
        getContext().getContentResolver().notifyChange(itemUri, null);

        return itemUri;
    }

    private String getTableName(Uri uri) {
        switch (sUriMatcher.match(uri)) {
        case USERS_TABLE:
        case USERS_TABLE_ID:
            return "Users";

        case ARTICLES_TABLE:
        case ARTICLES_TABLE_ID:
            return "Articles";

        case COMMENTS_TABLE:
        case COMMENTS_TABLE_ID:
            return "Comments";

        case ARTICLES_USERS_JOIN_TABLE:
            return "Articles a LEFT OUTER JOIN Users u ON (u._id = a.user_id)";

        case COMMENTS_USERS_JOIN_TABLE:
            return "Comments c LEFT OUTER JOIN Users u ON (u._id = c.user_id)";

        default:
            throw new IllegalArgumentException("Unknown URI " + uri);
        }
    }

    static {
        sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        sUriMatcher.addURI(AUTHORITY, "users", USERS_TABLE);
        sUriMatcher.addURI(AUTHORITY, "articles", ARTICLES_TABLE);
        sUriMatcher.addURI(AUTHORITY, "comments", COMMENTS_TABLE);
        sUriMatcher.addURI(AUTHORITY, "users" + "/#", USERS_TABLE_ID);
        sUriMatcher.addURI(AUTHORITY, "articles" + "/#", ARTICLES_TABLE_ID);
        sUriMatcher.addURI(AUTHORITY, "comments" + "/#", COMMENTS_TABLE_ID);
        sUriMatcher.addURI(AUTHORITY, "???", ARTICLES_USERS_JOIN_TABLE); // what uri here?
        sUriMatcher.addURI(AUTHORITY, "???", COMMENTS_USERS_JOIN_TABLE); // what uri here?
    }
}

What's the best URI scheme to notify all CursorAdapters listening on joined and non-joined queries every time I insert (or update) a row in the Users table?

In other words, if I add or update a new row in one of the tables, I want to send a single notification with getContext().getContentResolver().notifyChange(itemUri, null) so that all the CursorAdapters listening on any query (USERS_TABLE, ARTICLES_USERS_JOIN_TABLE, COMMENTS_USERS_JOIN_TABLE) receive a notification to update their content.

If this is not possible, is there an alternative way to notify all the observers?

like image 756
Lorenzo Polidori Avatar asked Apr 20 '12 08:04

Lorenzo Polidori


2 Answers

You can have special Uri's to query with:

    sUriMatcher.addURI(AUTHORITY, "articlesusers", ARTICLES_USERS_JOIN_TABLE);
    sUriMatcher.addURI(AUTHORITY, "commentsusers", COMMENTS_USERS_JOIN_TABLE);

But I can't think of a way to send a single notification. It seems your best choice is to send a notification for each Uri that refers to the table being modified. So your insert/update/delete methods would call notifyChange multiple times depending on the table affected. For changes to "users" it would be 3 notifications--users, articlesusers and commentsusers--since they all depend on the "users" table.

like image 72
satur9nine Avatar answered Oct 01 '22 13:10

satur9nine


As answered by prodaea, here is another alternative you can use for notification Uri. This is not a perfect solution, but it uses only one Uri for notification.

The solution is to use the main Uri without any table name (e.g:content://com.example.app.provider/) as the notification Uri in the query method for ARTICLES_USERS_JOIN_TABLE and COMMENTS_USERS_JOIN_TABLE. So, the related cursor will be notified whenever there is change in any table. There is one limitation though. That is, ARTICLES_USERS_JOIN_TABLE cursor will be notified even when there is change in Articles table.

For tables, Users' andArticles', you can use their specific Uris for notification.

like image 32
Ganesh Kumar Avatar answered Oct 01 '22 14:10

Ganesh Kumar