2011-11-22 33 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