I have tried Virgil Dobjanschi patterns A and B and made a conclusion that i want to use pattern C because in that case I can use google based code which take care about how to get may data to a device if i have non-stable network connection and so on... But I didn't find how to implement this in right way. Especially when you consider that the database is large enough to do syncing it all every time.
I did something like this
when CursorLoader did the query to show BizInbox the query returned the cursor and started SyncAdapter which should get data and stop its activity
But my problem is The SyncAdatert's onPerformSync start agin and again. And it looks like the endless cycle when the cursor reload its data and start onPerformSync and so on... And SyncAdapter do it in parallel without any queue as i hoped.
I should notice that the REST back end process data correctly and the second request which do onPerformSync have no data because it used If-Modified-Since http header.
It is obviously i did something wrong, becuase my SyncAdapter did not do sync backoff and did requests without end.
When i did if (!ContentResolver.isSyncActive(mConnectedAccount, Scheme.AUTHORITY)) { ContentResolver.requestSync( mConnectedAccount, // Sync account Scheme.AUTHORITY, // Content authority b); // Extras } else { Log.d(TAG, "> runSyncAdaterForClaimBizInbox skip starting adapter it is already active"); }
i was able to win the problem but the question remained how to perform this syncing if need to sync part of data - not all data ContentResolver.isSyncActive(mConnectedAccount, Scheme.AUTHORITY) - this method can not give info what part of data it is syncing and i don't have a queue for some reasons here. How to manage this problem ?
Please, help me to find the errors or may be somebody has the Pattern C compilable code to find out how to implement this pattern right.
Thanks in advance.
The code below:
public class ContentProvider {
@Override
public Cursor query(final Uri uri, final String[] projection, final String selection, final String[] selectionArgs, final String sortOrder) {
final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
final String orderBy = prepareQuery(uri, sortOrder, qb);
final SQLiteDatabase db = databaseHelper.getReadableDatabase();
final Cursor cursor = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
runSyncAdapterForQuery(uri);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
void runSyncAdapterForQuery(Uri uri) {
switch (uriMatcher.match(uri)) {
case BIZ_INBOX:
runSyncAdaterForBizInbox(METHOD_GET);
break;
}
}
void runSyncAdaterForBizInbox(String method) {
Bundle b = new Bundle();
b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
b.putString(METHOD_EXTRA, method);
b.putInt(RESOURCE_TYPE_EXTRA, RESOURCE_TYPE_BIZ_INBOX);
Account mConnectedAccount = new Account(BPalAccountGeneral.ACCOUNT_NAME, BPalAccountGeneral.ACCOUNT_TYPE);
//if (!ContentResolver.isSyncActive(mConnectedAccount, Scheme.AUTHORITY)) { Do i need this ? Where is Sync Adapters queue ?
ContentResolver.requestSync(
mConnectedAccount, // Sync account
Scheme.AUTHORITY, // Content authority
b); // Extras
//}
}
}
SyncAdapter
@Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
String accessToken = "some_token";
switch (resourceType) {
case UNKNOWN_RESOURCE_TYPE:
//do update all ...
break;
case RESOURCE_TYPE_BIZ_INBOX:
if (method.equals(METHOD_GET)) {
String url = "some_inbox_url"
GetBizInboxRequest request = new GetBizInboxRequest();
BizInboxDAO BizInboxDAO = new BizInboxDAO();
String lastModified = BizInboxDAO.getLastModified(getContext().getContentResolver());
RestMethodResult<RestListEntityContainer<BizInboxEntity>> restMethodResult =
request.getBizInboxItems(
url,
null,
lastModified,
accessToken);
int statusCode = restMethodResult.getStatusCode();
if (statusCode == MobileConstant.HTTP_OK) {
List<BizInboxEntity> serverBizInboxEntities = restMethodResult.getResource().getList();
for (BizInboxEntity entity : serverBizInboxEntities) {
BizInboxDAO.insertyData(getContext().getContentResolver(), entity);
syncResult.stats.numInserts++;
}
if(syncResult.stats.numInserts > 0) {
Log.d(TAG, "> onPerformSync BizInboxDAO.sendNotification ");
BizInboxDAO.sendNotification(getContext().getContentResolver());
}
} else {
syncResult.stats.numIoExceptions++;
syncResult.databaseError = true;
}
}
break;
default:
break;
}
}
I did the big mistake using the code above.
According the pattern C the Sync Adapter should check the current data state. I shouldn't call request sync directly from content provider. The ways are able to use are described in Android docs https://developer.android.com/training/sync-adapters/running-sync-adapter.html
If i called syncing as below // Disable sync backoff and ignore sync preferences. In other words...perform sync NOW! b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
you should remember what i put to comments:
Disable sync backoff and ignore sync preferences. In other words...perform sync NOW!
It means that request do not try to repeat itself if it is failed. and this code run its action in parallel. For example if you run this requestSync two tiles you will have two syncing threads as i found.
And the last main note: When i run requestSync from query it caused something like endless cycle, because after requestSync i do Cursot notifing which call re-query which run requestSync again and etc...