2011-11-22 35 views
3

我有我自己的ContentProviderSyncAdapter这两个工作正常。Android - 如何获取同步完成时的通知,请求与ContentResolver.requestSync同步()

我将它们设置为自动同步ContentResolver.setSyncAutomatically()这项工作。我也可以用开发工具 - >同步测试程序测试同步。

现在我想从我的应用程序请求同步(如果我们还没有数据)并在完成时得到通知,所以我可以更新界面(我正在同步显示带有标志的进度条) 。我正在用ContentResolver.requestSync()这样做,但是我还没有找到在同步完成时得到通知的方法。

有谁知道如何做到这一点?谢谢。

回答

3

使用addStatusChangeListener()会给你回电时,同步是SYNC_OBSERVER_TYPE_ACTIVESYNC_OBSERVER_TYPE_PENDING。没有完成的事件很奇怪。

这是Felix建议的workaround。他建议你应该抛弃ContentResolver以支持广播。

+1

谢谢,我用的解决方法。虽然这是一个迂回的做法。 SDK应该包含更好的方法。 – muscardinus

+0

@muscardinus我想你正在同步服务器和客户端之间的某种数据。每次应该有**服务器响应**,您可以**自己评估并发送本地广播**。 – JJD

5

addStatusChangeListener()确实通知您,当同步完成,在一个稍微迂回的方式,它只是这样做:SyncStatusObserver.onStatusChanged()被称为该国改变通知您。您必须拨打ContentResolver.isSyncPending()ContentResolver.isSyncActive()来检查新状态。

... 
ContentResolver.addStatusChangeListener(
     ContentResolver.SYNC_OBSERVER_TYPE_PENDING 
      | ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE, 
     new MySyncStatusObserver()); 
... 

private class MySyncStatusObserver implements SyncStatusObserver { 
    @Override 
    public void onStatusChanged(int which) { 
     if (which == ContentResolver.SYNC_OBSERVER_TYPE_PENDING) { 
      // 'Pending' state changed. 
      if (ContentResolver.isSyncPending(mAccount, MY_AUTHORITY)) { 
       // There is now a pending sync. 
      } else { 
       // There is no longer a pending sync. 
      } 
     } else if (which == ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE) { 
      // 'Active' state changed. 
      if (ContentResolver.isSyncActive(mAccount, MY_AUTHORITY)) { 
       // There is now an active sync. 
      } else { 
       // There is no longer an active sync. 
      } 
     } 
    } 
} 

另外也请注意:在我的测试我onStatusChanged()方法被调用四次,当我要求同步:

  1. 挂起被更改为true
  2. 未决更改为false
  3. 有效更改为真
  4. 有效更改为假

所以看起来有一个窗口之间的悬而未决和活动两个都设置为假即使活动同步即将开始。

6

这是一个完整的工作代码片段,带有javadocs,适合任何希望在解决方案中下降的人,而不必猜测如何将所有内容放在一起。它建立在Mark的回答上面。支持监控多个帐户同步。

import android.accounts.Account; 

import android.support.annotation.NonNull; 
import android.support.annotation.Nullable; 
/** 
* Sync status observer that reports back via a callback interface when syncing has begun 
* and finished. 
*/ 
public static class MySyncStatusObserver implements SyncStatusObserver { 
    /** 
    * Defines the various sync states for an account. 
    */ 
    private enum SyncState { 
     /** 
     * Indicates a sync is pending. 
     */ 
     PENDING, 
     /** 
     * Indicates a sync is no longer pending but isn't active yet. 
     */ 
     PENDING_ACTIVE, 
     /** 
     * Indicates a sync is active. 
     */ 
     ACTIVE, 
     /** 
     * Indicates syncing is finished. 
     */ 
     FINISHED 
    } 

    /** 
    * Lifecycle events. 
    */ 
    public interface Callback { 
     /** 
     * Indicates syncing of calendars has begun. 
     */ 
     void onSyncsStarted(); 

     /** 
     * Indicates syncing of calendars has finished. 
     */ 
     void onSyncsFinished(); 
    } 

    /** 
    * The original list of accounts that are being synced. 
    */ 
    @NonNull private final List<Account> mAccounts; 
    /** 
    * Map of accounts and their current sync states. 
    */ 
    private final Map<Account, SyncState> mAccountSyncState = 
      Collections.synchronizedMap(new HashMap<Account, SyncState>()); 

    /** 
    * The calendar authority we're listening for syncs on. 
    */ 
    @NonNull private final String mCalendarAuthority; 
    /** 
    * Callback implementation. 
    */ 
    @Nullable private final Callback mCallback; 

    /** 
    * {@code true} when a "sync started" callback has been called. 
    * 
    * <p>Keeps us from reporting this event more than once.</p> 
    */ 
    private boolean mSyncStartedReported; 
    /** 
    * Provider handle returned from 
    * {@link ContentResolver#addStatusChangeListener(int, SyncStatusObserver)} used to 
    * unregister for sync status changes. 
    */ 
    @Nullable private Object mProviderHandle; 

    /** 
    * Default constructor. 
    * 
    * @param accounts the accounts to monitor syncing for 
    * @param calendarAuthority the calendar authority for the syncs 
    * @param callback optional callback interface to receive events 
    */ 
    public MySyncStatusObserver(@NonNull final Account[] accounts, 
      @NonNull final String calendarAuthority, @Nullable final Callback callback) { 
     mAccounts = Lists.newArrayList(accounts); 
     mCalendarAuthority = calendarAuthority; 
     mCallback = callback; 
    } 

    /** 
    * Sets the provider handle to unregister for sync status changes with. 
    */ 
    public void setProviderHandle(@Nullable final Object providerHandle) { 
     mProviderHandle = providerHandle; 
    } 

    @Override 
    public void onStatusChanged(int which) { 
     for (final Account account : mAccounts) { 
      if (which == ContentResolver.SYNC_OBSERVER_TYPE_PENDING) { 
       if (ContentResolver.isSyncPending(account, mCalendarAuthority)) { 
        // There is now a pending sync. 
        mAccountSyncState.put(account, SyncState.PENDING); 
       } else { 
        // There is no longer a pending sync. 
        mAccountSyncState.put(account, SyncState.PENDING_ACTIVE); 
       } 
      } else if (which == ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE) { 
       if (ContentResolver.isSyncActive(account, mCalendarAuthority)) { 
        // There is now an active sync. 
        mAccountSyncState.put(account, SyncState.ACTIVE); 

        if (!mSyncStartedReported && mCallback != null) { 
         mCallback.onSyncsStarted(); 
         mSyncStartedReported = true; 
        } 
       } else { 
        // There is no longer an active sync. 
        mAccountSyncState.put(account, SyncState.FINISHED); 
       } 
      } 
     } 

     // We haven't finished processing sync states for all accounts yet 
     if (mAccounts.size() != mAccountSyncState.size()) return; 

     // Check if any accounts are not finished syncing yet. If so bail 
     for (final SyncState syncState : mAccountSyncState.values()) { 
      if (syncState != SyncState.FINISHED) return; 
     } 

     // 1. Unregister for sync status changes 
     if (mProviderHandle != null) { 
      ContentResolver.removeStatusChangeListener(mProviderHandle); 
     } 

     // 2. Report back that all syncs are finished 
     if (mCallback != null) { 
      mCallback.onSyncsFinished(); 
     } 
    } 
} 

下面是执行:

public class MyActivity extends Activity implements MySyncStatusObserver.Callback { 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.some_layout); 

     // Retrieve your accounts 
     final Account[] accounts = AccountManager.get(this).getAccountsByType("your_account_type"); 

     // Register for sync status changes 
     final MySyncStatusObserver observer = new MySyncStatusObserver(accounts, "the sync authority", this); 
     final Object providerHandle = ContentResolver.addStatusChangeListener(
      ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE | 
        ContentResolver.SYNC_OBSERVER_TYPE_PENDING, observer); 
     // Pass in the handle so the observer can unregister itself from events when finished. 
     // You could optionally save this handle at the Activity level but I prefer to 
     // encapsulate everything in the observer and let it handle everything 
     observer.setProviderHandle(providerHandle); 

     for (final Account account : accounts) { 
      // Request the sync 
      ContentResolver.requestSync(account, "the sync authority", null); 
     } 
    } 

    @Override 
    public void onSyncsStarted() { 
     // Show a refresh indicator if you need 
    } 

    @Override 
    public void onSyncsFinished() { 
     // Hide the refresh indicator if you need 
    } 
} 
+0

谢谢你的好例子。经过一些修改,我能够为我的特定情况做出这项工作 – Brandon