1

I have used applyBatch for insert,update and delete operation on SQLite database,there are more than 2000 entries for first time installation of app and for periodic sync too,due to large number of operation on database application get stop responding. applyBatch take approx 30-40 second for completion.

I have seen solution ContentResolver.bulkInsert (Uri url, ContentValues[] values) from Insertion of thousands of contact entries using applyBatch is slow

but it's for insert operation only,i have combination of query insert,update and delete.

i have also try to use AsyncTask

private class InsertTask extends AsyncTask<ArrayList<ContentProviderOperation>, Integer, Void> {

    @Override
    protected Void doInBackground(ArrayList<ContentProviderOperation>... params) {
        try {

            providerClient.applyBatch(params[0]);

        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (OperationApplicationException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);

    }
}

Thank you.

Community
  • 1
  • 1
  • Why AsyncTask is not useful? – Selvin Apr 26 '17 at 12:09
  • after using AsyncTask ,it still block the UI thread and on some device application get stop responding – Om Infowave Developers Apr 26 '17 at 12:17
  • 1
    not possible ... maybe another operation on ContentProvider blocks it (like: you run this AsyncTask and then (before it ends) you does query on it) – Selvin Apr 26 '17 at 12:18
  • As per contentProvider behavior after performing each operation it notifyChange().is it due to that? – Om Infowave Developers Apr 26 '17 at 12:20
  • yes, it may be a cause... modify your applyBatch to not do this until it's finish it may be tricky when you are using default implementation as it's calls insert/update/delete internally – Selvin Apr 26 '17 at 12:22
  • can you please explain in details,how can i prevent it from doing this and it's may requirement instant change in UI when something get changed – Om Infowave Developers Apr 26 '17 at 12:23
  • `applyBatch(operations) { var notifications = new List(); openTransaction(); foreach(var op in operations) { notifications.add(op.doOperationWithoutNotify()) } closeTransaction(); notifications = distinct(notifications); foreach(var n in notifications) n.notify();}` – Selvin Apr 26 '17 at 12:25

2 Answers2

2

Apply Batch

Normally the batch job would be executed in one transaction that spans all operations. But if your job contains many manipulations, the transaction might last quite a while and it might block other tasks from being executed.

The flip side of using batched operations is that a large batch may lock up the database for a long time preventing other applications from accessing data and potentially causing ANRs (“Application Not Responding” dialogs.)

To avoid such lockups of the database, make sure to insert “yield points” in the batch.

Yield Point:

A yield point indicates to the content provider that before executing the next operation it can commit the changes that have already been made, yield to other requests, open another transaction and continue processing operations. A yield point will not automatically commit the transaction, but only if there is another request waiting on the database.

So , You should set the withYieldAllowed() call at the beginning of each block of operations.

Hope It will help you !

Vikas Tiwari
  • 499
  • 4
  • 11
  • thank you for reply ,you mean i have add all operation like this ContentProviderOperation operation = ContentProviderOperation .newDelete(operationUri) .withYieldAllowed(true) .build(); – Om Infowave Developers Apr 26 '17 at 12:31
0

Here i am replying my own question,issue is due to many notifyChange() request on same URL with each entry,i am delayed the notification using below code

first override the applyBatch function

private boolean applyingBatch = false;
private List<Uri> delayedNotifications;

@NonNull
@Override
public ContentProviderResult[] applyBatch(@NonNull ArrayList<ContentProviderOperation> operations) throws OperationApplicationException {

    final SQLiteDatabase db = dbHelper.getWritableDatabase();
    db.beginTransaction();
    try {
        applyingBatch = true;
        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();

        applyingBatch = false;

        /*
        *
        * Notify to all URL where change it made
        * */
        synchronized (delayedNotifications) {
            for (Uri uri : delayedNotifications) {
                getContext().getContentResolver().notifyChange(uri, null);
            }
        }
        return results;
    } finally {
        applyingBatch = false;
        db.endTransaction();
    }

}

Then check if batch in operation progress then store the notification for delay otherwise sent notify

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

Check for notification on insert,update and delete

 @Override
public Uri insert(Uri uri, ContentValues values) {
    Uri result;
    long rawId;
    switch (URI_MATCHER.match(uri)) {

        case ALL_LIST:
            rawId = dbHelper.getWritableDatabase().insert(Table.TABLE_NAME, null, values);
            result = Station.getUriById(rawId);

        default:
            throw new IllegalArgumentException("Uri " + uri + " is not supported");
    }


    sendNotification(LIST.CONTENT_URI);
    return result;
}