问题 SyncAdapter运行动画 - 如何知道SyncAdapter是否正在主动同步


我想在ActionBar中显示ProgressBar,而我的SyncAdapter正在主动地将内容与Web同步。

我试过用过 SyncStatusObserver 和...一起 ContentProvider.addStatusChangeListener。但是,我无法检查SyncAdapter是否正在运行。我只能检查:

  1. SyncAdapter正在等待使用 ContentResolver.isSyncPending
  2. SyncAdapter正在等待或正在使用 ContentResolver.isSyncActive

这些标志可以合并: !isSyncPending && isSyncActive 这样就可以检查SyncAdapter是否正在正常工作,并且没有任何待处理的工作。但是,在某些情况下,SyncAdapter正在正常工作,并有第二个等待它的待处理请求。

看起来很简单,但我无法找到解决这个问题的方法。在SyncAdapter未运行时显示ProgressBar会给用户留下同步非常慢的印象。让它不显示ProgressBar会让用户认为没有发生任何事情。

代码中的上述解决方案如下所示。我们在activity.onResume中注册观察者:

 int mask = ContentResolver.SYNC_OBSERVER_TYPE_PENDING | ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE;
 syncHandle = ContentResolver.addStatusChangeListener(mask, syncObserver);

syncObserver在这里定义为:

syncObserver = new SyncStatusObserver()
{
    @Override
    public void onStatusChanged(int which)
    {
        Account account = getSomeAccount();
        boolean syncActive = ContentResolver.isSyncActive(account, CONTENT_AUTHORITY);
        boolean syncPending = ContentResolver.isSyncPending(account, CONTENT_AUTHORITY);
        boolean isSynchronizing = syncActive && !syncPending;
        updateRefreshButtonState();
    }
}

6808
2017-10-16 12:40


起源



答案:


我终于找到了问题的解决方案。我们的想法是使用ContentResolver getCurrentSyncs() 要么 getCurrentSync() 方法,以可用的为准。以下方法将检查同步操作当前是否适用于帐户和权限。它需要API级别8(Froyo = Android 2.2)。

private static boolean isSyncActive(Account account, String authority)
{
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
    {
        return isSyncActiveHoneycomb(account, authority);
    } else
    {
        SyncInfo currentSync = ContentResolver.getCurrentSync();
        return currentSync != null && currentSync.account.equals(account) &&
               currentSync.authority.equals(authority);
    }
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private static boolean isSyncActiveHoneycomb(Account account, String authority)
{
    for(SyncInfo syncInfo : ContentResolver.getCurrentSyncs())
    {
        if(syncInfo.account.equals(account) &&
           syncInfo.authority.equals(authority))
        {
            return true;
        }
    }
    return false;
}

然后,Activity会注册更新 onResume() 并取消注册 onDestroy()。此外,还必须手动更新状态 onResume() 赶上现状。

这是一个实现这样做的实现。子类应该自己定义

  • 什么帐户使用(实施 getAccount()
  • 什么授权使用(该领域 CONTENT_AUTHORITY
  • 如何显示同步状态(实现 updateState(boolean isSynchronizing)

我希望将来可以帮助某人。

import android.accounts.Account;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.SyncInfo;
import android.content.SyncStatusObserver;
import android.os.Build;
import android.os.Bundle;

public abstract class SyncActivity extends Activity
{
    private static final String CONTENT_AUTHORITY = "com.example.authority";
    private Object syncHandle;
    private SyncStatusObserver observer;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        observer = new SyncStatusObserver()
        {
            @Override
            public void onStatusChanged(int which)
            {
                runOnUiThread(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        Account account = getAccount();
                        boolean isSynchronizing =
                                isSyncActive(account, CONTENT_AUTHORITY);
                        updateState(isSynchronizing);
                    }
                });
            }
        };
    }

    @Override
    protected void onResume()
    {
        super.onResume();

        // Refresh synchronization status
        observer.onStatusChanged(0);

        // Watch for synchronization status changes
        final int mask = ContentResolver.SYNC_OBSERVER_TYPE_PENDING |
                ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE;
        syncHandle = ContentResolver.addStatusChangeListener(mask, observer);
    }

    @Override
    protected void onPause()
    {
        super.onPause();

        // Remove our synchronization listener if registered
        if (syncHandle != null)
        {
            ContentResolver.removeStatusChangeListener(syncHandle);
            syncHandle = null;
        }
    }

    private static boolean isSyncActive(Account account, String authority)
    {
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
        {
            return isSyncActiveHoneycomb(account, authority);
        } else
        {
            SyncInfo currentSync = ContentResolver.getCurrentSync();
            return currentSync != null && currentSync.account.equals(account) 
                    && currentSync.authority.equals(authority);
        }
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private static boolean isSyncActiveHoneycomb(Account account,
                                                         String authority)
    {
        for(SyncInfo syncInfo : ContentResolver.getCurrentSyncs())
        {
            if(syncInfo.account.equals(account) &&
                    syncInfo.authority.equals(authority))
            {
                return true;
            }
        }
        return false;
    }

    protected abstract Account getAccount();
    protected abstract void updateState(boolean isSynchronizing);
}

16
2017-11-01 14:38



对于Eclair,Froyo,Gingerbread,上述解决方案仅在设备只有一个帐户时才有效。如果设备上存在多个帐户,则第一个帐户只会同步;其余的都被忽略了。 - ChuongPham


答案:


我终于找到了问题的解决方案。我们的想法是使用ContentResolver getCurrentSyncs() 要么 getCurrentSync() 方法,以可用的为准。以下方法将检查同步操作当前是否适用于帐户和权限。它需要API级别8(Froyo = Android 2.2)。

private static boolean isSyncActive(Account account, String authority)
{
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
    {
        return isSyncActiveHoneycomb(account, authority);
    } else
    {
        SyncInfo currentSync = ContentResolver.getCurrentSync();
        return currentSync != null && currentSync.account.equals(account) &&
               currentSync.authority.equals(authority);
    }
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private static boolean isSyncActiveHoneycomb(Account account, String authority)
{
    for(SyncInfo syncInfo : ContentResolver.getCurrentSyncs())
    {
        if(syncInfo.account.equals(account) &&
           syncInfo.authority.equals(authority))
        {
            return true;
        }
    }
    return false;
}

然后,Activity会注册更新 onResume() 并取消注册 onDestroy()。此外,还必须手动更新状态 onResume() 赶上现状。

这是一个实现这样做的实现。子类应该自己定义

  • 什么帐户使用(实施 getAccount()
  • 什么授权使用(该领域 CONTENT_AUTHORITY
  • 如何显示同步状态(实现 updateState(boolean isSynchronizing)

我希望将来可以帮助某人。

import android.accounts.Account;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.SyncInfo;
import android.content.SyncStatusObserver;
import android.os.Build;
import android.os.Bundle;

public abstract class SyncActivity extends Activity
{
    private static final String CONTENT_AUTHORITY = "com.example.authority";
    private Object syncHandle;
    private SyncStatusObserver observer;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        observer = new SyncStatusObserver()
        {
            @Override
            public void onStatusChanged(int which)
            {
                runOnUiThread(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        Account account = getAccount();
                        boolean isSynchronizing =
                                isSyncActive(account, CONTENT_AUTHORITY);
                        updateState(isSynchronizing);
                    }
                });
            }
        };
    }

    @Override
    protected void onResume()
    {
        super.onResume();

        // Refresh synchronization status
        observer.onStatusChanged(0);

        // Watch for synchronization status changes
        final int mask = ContentResolver.SYNC_OBSERVER_TYPE_PENDING |
                ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE;
        syncHandle = ContentResolver.addStatusChangeListener(mask, observer);
    }

    @Override
    protected void onPause()
    {
        super.onPause();

        // Remove our synchronization listener if registered
        if (syncHandle != null)
        {
            ContentResolver.removeStatusChangeListener(syncHandle);
            syncHandle = null;
        }
    }

    private static boolean isSyncActive(Account account, String authority)
    {
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
        {
            return isSyncActiveHoneycomb(account, authority);
        } else
        {
            SyncInfo currentSync = ContentResolver.getCurrentSync();
            return currentSync != null && currentSync.account.equals(account) 
                    && currentSync.authority.equals(authority);
        }
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private static boolean isSyncActiveHoneycomb(Account account,
                                                         String authority)
    {
        for(SyncInfo syncInfo : ContentResolver.getCurrentSyncs())
        {
            if(syncInfo.account.equals(account) &&
                    syncInfo.authority.equals(authority))
            {
                return true;
            }
        }
        return false;
    }

    protected abstract Account getAccount();
    protected abstract void updateState(boolean isSynchronizing);
}

16
2017-11-01 14:38



对于Eclair,Froyo,Gingerbread,上述解决方案仅在设备只有一个帐户时才有效。如果设备上存在多个帐户,则第一个帐户只会同步;其余的都被忽略了。 - ChuongPham