Android ContentProvider calls bursts of setNotificationUri() to CursorAdapter when many rows are inserted with a batch operation

后端 未结 3 787
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-02-08 01:51

I have a custom ContentProvider which manages the access to a SQLite database. To load the content of a database table in a ListFragment, I use the

相关标签:
3条回答
  • 2021-02-08 02:30

    I found a simpler solution that I discovered from Google's I/O app. You just have to override the applyBatch method in your ContentProvider and perform all the operations in a transaction. Notifications are not sent until the transaction commits, which results in minimizing the number of ContentProvider change-notifications that are sent out:

    @Override
    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
            throws OperationApplicationException {
    
        final SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
        db.beginTransaction();
        try {
            final int numOperations = operations.size();
            final ContentProviderResult[] results = new ContentProviderResult[numOperations];
            for (int i = 0; i < numOperations; i++) {
                results[i] = operations.get(i).apply(this, results, i);
            }
            db.setTransactionSuccessful();
            return results;
        } finally {
            db.endTransaction();
        }
    }
    
    0 讨论(0)
  • 2021-02-08 02:47

    In a comment to the original answer, Jens directed us towards SQLiteContentProvider in the AOSP. One reason why this isn't (yet?) in the SDK may be that the AOSP seems to contain multiple variations of this code.

    For example com.android.browser.provider.SQLiteContentProvider seems to be a slightly more complete solution, incorporating the "delayed notifications" principle proposed by Phillip Fitzsimmons while keeping the provider thread-safe by using a ThreadLocal for the batch-flag and synchronizing access to the delayed notification set.

    Yet even though access to the set of URI's to be notified of change is synchronized, I can still imagine that race conditions may occur. For example if a long operation posts some notifications, then gets overtaken by a smaller batch operation, which fires the notifications and clears the set before the first operation commits.

    Still, the above version seems to be the best bet as a reference when implementing your own provider.

    0 讨论(0)
  • 2021-02-08 02:53

    To address this exact problem, I overrode applyBatch and set a flag which blocked other methods from sending notifications.

        volatile boolean applyingBatch=false;
        public ContentProviderResult[] applyBatch(
            ArrayList<ContentProviderOperation> operations)
            throws OperationApplicationException {
        applyingBatch=true;
        ContentProviderResult[] result;
        try {
            result = super.applyBatch(operations);
        } catch (OperationApplicationException e) {
            throw e;
        }
        applyingBatch=false;
        synchronized (delayedNotifications) {
            for (Uri uri : delayedNotifications) {
                getContext().getContentResolver().notifyChange(uri, null);
            }
        }
        return result;
    }
    

    I exposed a method to "store" notifications to be sent when the batch was complete:

    protected void sendNotification(Uri uri) {
        if (applyingBatch) {
            if (delayedNotifications==null) {
                delayedNotifications=new ArrayList<Uri>();
            }
            synchronized (delayedNotifications) {
                if (!delayedNotifications.contains(uri)) {
                    delayedNotifications.add(uri);
                }
            }
        } else {
            getContext().getContentResolver().notifyChange(uri, null);
        }
    }
    

    And any methods that send notifications employ sendNotification, rather than directly firing a notification.

    There may well be better ways of doing this - it certainly seems as though they're ought to be - but that's what I did.

    0 讨论(0)
提交回复
热议问题