Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent network sync loop when syncing from network in Android ContentProvider

I'm writing my own ContentProvider which will be synced to a web service using a SyncAdapter.

Problem happens when the sync adapter is modifying the content provider's data the provider triggers a network sync when internally calling getContentResolver().notifyChange causing a sync loop.

The notifyChange with the network sync flag is required for when a client application does the modification but should be avoided when the sync adapter is modifying.

How can one, inside a contentprovider, easly tell if it's being used by a client application (which should trigger network sync upon modification) or by a sync adapter (which should not trigger network sync).

Currently I'm using different CONTENT_URI's (sync adapter accesses the data using a CONTENT_URI_NO_SYNC and client apps using a CONTENT_URI) to be able to distinguish between the two types of access and set the network sync flag accordingly.

like image 253
m1h4 Avatar asked Jul 05 '11 20:07

m1h4


1 Answers

Watch this video about REST API usage in SyncAdapters.

The method they discuss is to add a set of metadata flags columns to the database. This allows us to do 3 things.

  1. The flags themselves allow the SyncAdapter to determine the rows that need changes and what those changes are. How do you tell the difference between a locally created row and a locally modified row? Furthermore how do you know which REST API call to make? If you just delete a row, how does your SyncAdapter know the row to be deleted if the data is now gone? Instead, set the "Should be deleted" flag, and then, when the SyncAdapter runs, it knows to push a delete to the server.

  2. The flags allow your CursorAdapter to modify the view that is created (like adding a Spinner to show that "This row is being synced")

  3. Finally, and this they don't point out, the flags allow you to tell why the row is being modified. If none of the flags are set and the row changes, it must have been because of an update from the server. Therefore, no need to sync to network.

So, the two workflows are as follows:

Local change

  1. App creates new row. Row "create" flag is true.
  2. ContentProvider stores the row, sees create flag and so it calls notifyChange(...,true);
  3. Sync to network = true (the final parameter) causes SyncAdapter to fire.
  4. SyncAdapter scans the database, finds the row with create flag set and performs appropriate server action. After success, SyncAdapter clears the flag.(row update on ContentProvivder)
  5. ContentProvider sees the flag clear, no flags are left set, so it calls notifyChange(...,false);
  6. ContentObservers see the flag change, update to look like "sync finished"

All these steps are equivalent for update / delete -- one flag per syncable row for each of create/update/delete. Also notice the other win -- what if "Create" fails temporarily? server down... How do you know to retry? -- Simple, you don't clear the "Create" flag and you see it 15 minutes later.

Remote Change

  1. SyncAdapter fires due to periodic sync.
  2. SyncAdapter fetches an update from the server. Pushes changes into the database. Doesn't set any flags. ContentProvider sees the lack of flags, knows the change must have come from the server (or isn't a database change that needs to be pushed to the server), so it calls notifyChange(...,false);
  3. ContentObservers see the content change and so they update with new row data
like image 163
jcwenger Avatar answered Sep 19 '22 01:09

jcwenger