Home » Android » android – How to know when sync is finished?

android – How to know when sync is finished?

Posted by: admin April 23, 2020 Leave a comment

Questions:

I have implemented a sync adapter and I want to get a callback when it finishes in my activity. I have tried using ContentResolver.addStatusChangeListener, but I am only getting callbacks when the sync is pending / active. Here’s some relevant code from my activity:

@Override
protected void onResume() {
    super.onResume();
    final int mask = ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE | ContentResolver.SYNC_OBSERVER_TYPE_PENDING;
    syncObserverHandle = ContentResolver.addStatusChangeListener(mask, syncStatusObserver);
}

@Override
protected void onPause() {
    super.onPause();
    if (syncObserverHandle != null) {
        ContentResolver.removeStatusChangeListener(syncObserverHandle);
        syncObserverHandle = null;
    }
}

private SyncStatusObserver syncStatusObserver = new SyncStatusObserver() {

    @Override
    public void onStatusChanged(int which) {
        AccountManager am = AccountManager.get(TodosActivity.this);
        Account a = am.getAccountsByType(Const.ACCOUNT_TYPE)[0];

        Log.d(Const.TAG, "Sync status changed: " + which);

        if (!ContentResolver.isSyncActive(a, DataProvider.AUTHORITY) &&
                !ContentResolver.isSyncPending(a, DataProvider.AUTHORITY)) {
            Log.d(Const.TAG, "Sync finished, should refresh nao!!");
        }
    }
};

However, the if in the onStatusChanged method is never valid. I have taken this example from the JumpNote demo where it works probably because it is also called manually in onResume(), so it’s probably never called by the system when the sync is finished. Or is it, and I’m doing something wrong? Here’s what I get in logcat:

D/MYAPP (10903): Sync status changed: 2
D/MYAPP (10903): Sync status changed: 2
D/MYAPP (10903): Sync status changed: 4
D/MYAPP (10981): --> DataSyncAdapter.onPerformSync()
D/MYAPP (10981): <-- DataSyncAdapter.onPerformSync()
D/MYAPP (10903): Sync status changed: 4

So, it seems that I could rely on the second SYNC_OBSERVER_TYPE_ACTIVE (4) status change to refresh my data, but that seems really ugly. Any ideas?

How to&Answers:

One solution that I have found is to ditch the ContentResolver completely, and implement my own broadcast. Basically, add this in the sync adapter, at the end of onPerformSync:

Intent i = new Intent(SYNC_FINISHED);
sendBroadcast(i);

And this in the activity:

@Override
protected void onResume() {
    super.onResume();
    registerReceiver(syncFinishedReceiver, new IntentFilter(DataSyncService.SYNC_FINISHED));
}

@Override
protected void onPause() {
    super.onPause();
    unregisterReceiver(syncFinishedReceiver);
}

private BroadcastReceiver syncFinishedReceiver = new BroadcastReceiver() {

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(Const.TAG, "Sync finished, should refresh nao!!");
    }
};

This seems to work just fine, however I was hoping to find something in the SDK that directly notify me when a sync is finished.

Answer:

Strange, it does work for me.

In my Activity I have:

  @Override
  protected void onPause() {
    super.onPause();
    ContentResolver.removeStatusChangeListener(mContentProviderHandle);
  }

  @Override
  protected void onResume() {
    super.onResume();
    mContentProviderHandle = ContentResolver.addStatusChangeListener(
        ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE, this);
  }

  @Override
  public void onStatusChanged(int which) {
    AccountManager accountManager = AccountManager.get(this);
    Account[] accounts = accountManager
        .getAccountsByType(AuthenticatorActivity.PARAM_ACCOUNT_TYPE);

    if (accounts.length <= 0) {
      return;
    }

    updateRefresh(ContentResolver.isSyncActive(accounts[0],
        MyContentProvider.AUTHORITY));
  }

  // Since onStatusChanged() is not called from the main thread
  // I need to update the ui in the ui-thread.
  private void updateRefresh(final boolean isSyncing) {
    runOnUiThread(new Runnable() {

      @Override
      public void run() {
        if (isSyncing) {
          mRefreshMenu.setActionView(R.layout.menu_item_refresh);
        } else {
          mRefreshMenu.setActionView(null);
        }
      }
    });
  }

Answer:

I was working on something similar, and I have one more Log.d at the end of the onStatusChanged function, and it wasn’t executing!

So after 15 minutes of debugging and trial on error, I realize that I need to add the READ_SYNC_STATS permission, I already have the GET_ACCOUNTS permission though. So, please check that you get the right permission, its not that the “if” doesn’t becomes true, its that it bails all the time.