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

2

11

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?

Shepp answered 20/4, 2012 at 8:49 Comment(0)
S
5

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.

Stephanestephani answered 26/7, 2012 at 3:18 Comment(0)
I
1

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.

Insist answered 28/9, 2015 at 12:18 Comment(1)
way more elegant than calling notifyChange after every insert/update/delete as suggested in satur9nine's answer!Otiliaotina

© 2022 - 2024 — McMap. All rights reserved.