lundi 28 septembre 2015

Content Provider doesn't update SQLite db from SyncAdapter, when it called by system

I wrote SyncAdapter with Content Provider. It works fine, when I call sync manually:

ContentResolver.requestSync(AccountHelper.getAccount(),
                            SvoDataContract.AUTHORITY,
                            new Bundle());

But SyncAdapter doesn't update DB when called by the system (when we turn on wi-fi for example). It seems ContentProvider applies changes to cache or another DB, because I use logs before and after update and I can see that read operation returns exactly my values, that I used for updating. But when I open my SQLite db file, there is no changes. Very strange behavior.

My SyncAdapter:

public class SyncAdapter extends AbstractThreadedSyncAdapter {
    private static final String TAG          = "SVOSyncAdapter";
    private static final int REQUEST_TIMEOUT = 30000;
    private static final int HTTP_NOT_FOUND  = 404;

    public SyncAdapter(Context context, boolean autoInitialize) {
        super(context, autoInitialize);
        DebugLog.i(TAG, context.getApplicationInfo().toString());
    }

    @Override
    public void onPerformSync(Account account,
                              Bundle extras,
                              String authority,
                              ContentProviderClient provider,
                              SyncResult syncResult) {    
        syncJourney(provider, getContext());
    }

    private void syncJourney(final ContentProviderClient provider, final Context context) {
        List<Journey> journeyList = JourneyDAO.read(context, true);

        syncEntities(journeyList, new IOnSyncCallback<Journey>() {
            @Override
            public void onSuccess(Journey entity, Journey modifiedEntity) {
                if (entity.getState() == SqlContract.BackendSynchronized.State.CREATED) {
                    entity.setId(modifiedEntity.getId());
                }
                entity.setNeedSync(false);
                if (entity.getId() != null) {
                    DebugLog.i(TAG, String.format("Updating journey: %s", entity.getId().toString()));
                }

                String where = String.format("%s=?", BaseColumns._ID);
                String[] args = {String.valueOf(entity.getLocalId())};
                try {
                    provider.update(SvoDataContract.Journey.CONTENT_URI,
                                    JourneyDAO.createContentValues(entity),
                                    where,
                                    args);

                    Cursor cursor = provider.query(SvoDataContract.Journey.CONTENT_URI, null, where, args, null);
                    if (cursor.moveToFirst()) {
                        DebugLog.i(TAG,
                                   String.format("After update: %d",
                                                 CursorHelper.getInt(cursor,
                                                                     SqlContract.Journey.COLUMN_SERVER_ID)));
                    } // It looks like we wrote data to DB successfully
                    cursor.close();
                } catch (RemoteException re) {
                    DebugLog.i(TAG, "Remote exception");
                }
            }

            @Override
            public void onFail(Journey entity, int networkError) {
                if (networkError == HTTP_NOT_FOUND) {
                    DebugLog.i(TAG, "fail");
                    entity.setNeedSync(false);

                    String where = String.format("%s=?", BaseColumns._ID);
                    String[] args = {String.valueOf(entity.getLocalId())};
                    try {
                        provider.update(SvoDataContract.Journey.CONTENT_URI,
                                        JourneyDAO.createContentValues(entity),
                                        where,
                                        args);
                    } catch (RemoteException re) {
                        DebugLog.i(TAG, "Remote exception");
                    }
                }
            }
        });
    }

    private <T extends ISyncEntity> void syncEntities(final List<T> entities, final IOnSyncCallback<T> syncCallback) {
         // some work with Backend
    }

    private interface IOnSyncCallback<T> {
        void onSuccess(T entity, T modifiedEntity);
        void onFail(T entity, int networkError);
    }
}

My manifest:

<provider
    android:name=".provider.SvoContentProvider"
    android:authorities="ru.agencyprime.svo.provider"
    android:enabled="true"
    android:syncable="true"
    android:multiprocess="true"
    android:exported="true" >
</provider>
    <service
        android:name=".sync.account.AuthenticatorService"
        android:exported="false">
        <intent-filter>
            <action android:name="android.accounts.AccountAuthenticator" />
        </intent-filter>
        <meta-data
            android:name="android.accounts.AccountAuthenticator"
            android:resource="@xml/authenticator" />
    </service>
    <service
        android:name=".sync.SyncService"
        android:exported="false"
        android:process=":sync">
        <intent-filter>
            <action android:name="android.content.SyncAdapter" />
        </intent-filter>
        <meta-data
            android:name="android.content.SyncAdapter"
            android:resource="@xml/sync_adapter" />
    </service>

SyncAdapter registration:

public class AccountHelper {
    public static final String ACCOUNT_TYPE = "ru.agencyprime.svo.account";

    private static Account account;

    private AccountHelper() {
    }

    public static void register(Context context) {
        final AccountManager am = AccountManager.get(context);
        if (account == null) {
            account = new Account(context.getString(R.string.sync_my_journeys), ACCOUNT_TYPE);
        }
        if (am.addAccountExplicitly(account, context.getPackageName(), new Bundle())) {
            ContentResolver.setSyncAutomatically(account, SvoDataContract.AUTHORITY, true);
        }
    }

    public static Account getAccount() {
        return account;
    }
}

What could be wrong? I am confused that it works fine with manual synchronisation.

Aucun commentaire:

Enregistrer un commentaire