В настоящее время я работаю над Android-приложением, которое полагается на SyncAdapter
для обновления своего контента с сервера. Я в основном соблюдал следующие инструкции: https://developer.android.com/training/sync-adapters/creating-sync-adapter.html
Это отлично работало до недавнего времени. Я знаю, что это может показаться глупым, но я честно понятия не имею, как я это испортил: (
Настройка
У меня есть один ContentProvider
и, следовательно, один SyncAdapter
и один аккаунт для всех элементов, которые я хочу синхронизировать. Я использую целочисленные флаги, чтобы определить, какой элемент должен быть синхронизирован:
public static final int EVENTS = 0x1;
public static final int NEWS = 0x2;
public static final int SUBSTITUTIONS = 0x4;
public static final int TEACHERS = 0x8;
public static final int ALL = 0xF;
Итак, в моем onPerformSync
у меня есть что-то вроде:
ArrayList<ContentProviderOperation> batchList = new ArrayList<>();
int which = extras.getInt(SYNC.ARG, SYNC.ALL);
if((which & SYNC.NEWS) == SYNC.NEWS) { syncNews(provider, batchList, syncResult, 0, 1); }
if((which & SYNC.EVENTS) == SYNC.EVENTS) { syncEvents(provider, batchList, syncResult); }
if((which & SYNC.TEACHERS) == SYNC.TEACHERS) { syncTeachers(provider, batchList, syncResult); }
if((which & SYNC.SUBSTITUTIONS) == SYNC.SUBSTITUTIONS) { syncSubstitutions(provider, batchList, syncResult); }
Log.i(TAG, "Merge solution ready. Applying batch update to database...");
provider.applyBatch(batchList);
Поскольку я также хочу, чтобы пользователь мог принудительно обновить, я использую SwipeRefreshLayout
для запуска службы синхронизации:
@Override
public void onRefresh() {
Log.d(TAG, "Force refresh triggered!");
SyncUtils.triggerRefresh(SyncAdapter.SYNC.NEWS | SyncAdapter.SYNC.EVENTS);
}
Я также хочу отслеживать состояние синхронизации, поэтому я регистрирую/отменю регистрацию SyncStatusObserver
в моем фрагменте onResume
/onPause
:
private final SyncStatusObserver syncStatusObserver = new SyncStatusObserver() {
@Override
public void onStatusChanged(int which) {
Account account = AuthenticatorService.getAccount(SyncUtils.ACCOUNT_TYPE);
boolean syncActive = ContentResolver.isSyncActive(account, DataProvider.AUTHORITY);
boolean syncPending = ContentResolver.isSyncPending(account, DataProvider.AUTHORITY);
final boolean refresh = syncActive || syncPending;
Log.d(TAG, "Status change detected. Active: %b, pending: %b, refreshing: %b", syncActive, syncPending, refresh);
swipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
swipeRefreshLayout.setRefreshing(refresh);
}
});
}
};
Проблема
Всякий раз, когда я запускаю приложение, Refresh layout
указывает, что синхронизация активна. Я зарегистрировал почти все и выяснил, что синхронизация находится в состоянии ожидания. Всякий раз, когда я пытаюсь принудительно обновить, синхронизация будет либо
- Станьте активным, делайте все, а затем возвращайтесь в ожидающий или
- Просто не становитесь активными и оставайтесь в ожидающем режиме навсегда
Здесь пример журнала:
D/HomeFragment﹕ Status change detected. Active: false, pending: true, refreshing: true
D/HomeFragment﹕ Status change detected. Active: false, pending: true, refreshing: true
D/HomeFragment﹕ Status change detected. Active: true, pending: true, refreshing: true
D/HomeFragment﹕ Status change detected. Active: false, pending: true, refreshing: true
Как вы можете видеть, никогда не будет Active: false, pending: false
для указания завершения синхронизации. Это действительно измельчает мои механизмы.
Еще один код
Я делаю первоначальную настройку учетной записи-заглушки (и периодических синхронизаций) в классе приложения:
public static void createSyncAccount(Context context) {
boolean newAccount = false;
boolean setupComplete = PreferenceManager
.getDefaultSharedPreferences(context).getBoolean(PREF_SETUP_COMPLETE, false);
// Create account, if it missing. (Either first run, or user has deleted account.)
Account account = AuthenticatorService.getAccount(ACCOUNT_TYPE);
AccountManager accountManager =
(AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
if (accountManager.addAccountExplicitly(account, null, null)) {
// Inform the system that this account supports sync
ContentResolver.setIsSyncable(account, DataProvider.AUTHORITY, 1);
// Inform the system that this account is eligible for auto sync when the network is up
ContentResolver.setSyncAutomatically(account, DataProvider.AUTHORITY, true);
// Recommend a schedule for automatic synchronization. The system may modify this based
// on other scheduled syncs and network utilization.
requestPeriodic(account, SYNC.EVENTS, 172800);
requestPeriodic(account, SYNC.NEWS, 604800);
requestPeriodic(account, SYNC.SUBSTITUTIONS, 1800);
requestPeriodic(account, SYNC.TEACHERS, 2419200);
newAccount = true;
}
// Schedule an initial sync if we detect problems with either our account or our local
// data has been deleted. (Note that it possible to clear app data WITHOUT affecting
// the account list, so wee need to check both.)
if (newAccount || !setupComplete) {
triggerRefresh(SYNC.ALL);
PreferenceManager.getDefaultSharedPreferences(context).edit()
.putBoolean(PREF_SETUP_COMPLETE, true).commit();
}
}
Где requestPeriodic()
следующее:
public static void requestPeriodic(Account account, int which, long seconds) {
Bundle options = new Bundle();
options.putInt(SYNC.ARG, which);
ContentResolver.addPeriodicSync(account,
DataProvider.AUTHORITY, options, seconds);
}
И мой triggerRefresh()
выглядит так:
public static void triggerRefresh(int which) {
Log.d(TAG, "Force refresh triggered for id: %d", which);
Bundle options = new Bundle();
options.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
options.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
options.putInt(SYNC.ARG, which);
ContentResolver.requestSync(
AuthenticatorService.getAccount(ACCOUNT_TYPE),
DataProvider.AUTHORITY,
options
);
}
Кто-нибудь сталкивался с подобными проблемами или идеей о том, что я сделал неправильно?
Обновление 1
Я попытался изменить способ использования SyncStatusObserver
. Теперь я получаю информацию из параметра флага which
следующим образом:
private final SyncStatusObserver syncStatusObserver = new SyncStatusObserver() {
@Override
public void onStatusChanged(int which) {
boolean syncActive = (which & ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE) == ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE;
boolean syncPending = (which & ContentResolver.SYNC_OBSERVER_TYPE_PENDING) == ContentResolver.SYNC_OBSERVER_TYPE_PENDING;
boolean refreshing = syncActive || syncPending;
// update UI...
}
};
Когда я это делаю, состояние pending
кажется правильным, поэтому его возвращение false
, как только адаптер начнет процесс синхронизации, , но, теперь адаптер остается активным и у меня одинаковый неверный результат для boolean refreshing
, как всегда.:/