`

Android Email 新建邮件时一直显示“waiting for sync”

阅读更多
当用户在Setting中清除了Email的data,再返回到Email进行新建邮件就会一直显示“waiting for sync”.从字面的意思就是要进行同步,我跑到Setting-->Account中手动同步了账号,再新建邮件,发现问题仍一直存在。

1.导出Email APP的数据查看Account tables的数据发现,发生问题和正常时数据是一致的

2.通过搜索“waiting for sync”定位到显示该UI的是在ComposeActivity.java中
   private void showWaitFragment(Account account) {
        WaitFragment fragment = getWaitFragment();
        if (fragment != null) {
            fragment.updateAccount(account);
        } else {
            findViewById(R.id.wait).setVisibility(View.VISIBLE);
            replaceFragment(WaitFragment.newInstance(account, false /* expectingMessages */),
                    FragmentTransaction.TRANSIT_FRAGMENT_OPEN, TAG_WAIT);
        }
    }


3.接着分析何时会调用showWaitFragment,发现是当Account的状态处于
INITIAL_SYNC_NEEDED或者ACCOUNT_INITIALIZATION_REQUIRED
public boolean isAccountReady() {
        return !isAccountInitializationRequired() && !isAccountSyncRequired();
    }
   public boolean isAccountSyncRequired() {
        return (syncStatus & SyncStatus.INITIAL_SYNC_NEEDED) == SyncStatus.INITIAL_SYNC_NEEDED;
    }

    public boolean isAccountInitializationRequired() {
        return (syncStatus & SyncStatus.ACCOUNT_INITIALIZATION_REQUIRED) ==
                SyncStatus.ACCOUNT_INITIALIZATION_REQUIRED;
    }


4.接着分析syncStatus的值的来源
private String genQueryAccount(String[] uiProjection, String id) {
 ....
if (projectionColumns.contains(UIProvider.AccountColumns.SYNC_STATUS)) {
            if (inboxMailboxId != Mailbox.NO_MAILBOX) {
                values.put(UIProvider.AccountColumns.SYNC_STATUS, UIProvider.SyncStatus.NO_SYNC);
            } else {
                values.put(UIProvider.AccountColumns.SYNC_STATUS,
                        UIProvider.SyncStatus.INITIAL_SYNC_NEEDED);
            }
        }
...

通过加log打印发现account最后SYNC_STATUS都会修改为NO_SYNC,为何在ComposeActivity查询出来的仍旧是INITIAL_SYNC_NEEDED

5.返回ComposeActivity分析Account的加载过程
首先是checkValidAccounts中
 private void checkValidAccounts() {
        final Account[] allAccounts = AccountUtils.getAccounts(this);
        if (allAccounts == null || allAccounts.length == 0) {
            final Intent noAccountIntent = MailAppProvider.getNoAccountIntent(this);
            if (noAccountIntent != null) {
                mAccounts = null;
                startActivityForResult(noAccountIntent, RESULT_CREATE_ACCOUNT);
            }
        } else {
            // If none of the accounts are syncing, setup a watcher.
            boolean anySyncing = false;
            for (Account a : allAccounts) {
                if (a.isAccountReady()) {
                    anySyncing = true;
                    break;
                }
            }
            if (!anySyncing) {
                // There are accounts, but none are sync'd, which is just like having no accounts.
                mAccounts = null;
                getLoaderManager().initLoader(LOADER_ACCOUNT_CURSOR, null, this);
                return;
            }
            mAccounts = AccountUtils.getSyncingAccounts(this);
            finishCreate();
        }
    }

调用AccountUtils.getAccounts查询出Account
    public static Account[] getAccounts(Context context) {
        final ContentResolver resolver = context.getContentResolver();
        Cursor accountsCursor = null;
        final List<Account> accounts = Lists.newArrayList();
        try {
            accountsCursor = resolver.query(MailAppProvider.getAccountsUri(),
                    UIProvider.ACCOUNTS_PROJECTION, null, null, null);
            if (accountsCursor != null) {
                while (accountsCursor.moveToNext()) {
                    accounts.add(Account.builder().buildFrom(accountsCursor));
                }
            }
        } finally {
            if (accountsCursor != null) {
                accountsCursor.close();
            }
        }
        return accounts.toArray(new Account[accounts.size()]);
    }
}

此处调用查询的uri是content://com.android.mail.accountcache/它对应的ContentProvider是UnifiedAccountCacheProvider,真正实现的是MailAppProvider

6.分析发现MailAppProvider会将从EmailProvider查询到结果缓存到SharePreference和mAccountCache中,当query只是从mAccountCache获取,而MailAppProvider会在onCreate的时候就会从EmailProvider中获取结果,并且此时account并没有同步完成导致缓存的结果的syncstatus是INITIAL_SYNC_NEEDED

7.那问题点找到了,该如何解决呢?如何让ComposeActivity获取到最新的状态。又返回到了checkValidAccounts中,发现当isAccountReady 返回false时,会重新getLoaderManager().initLoader(LOADER_ACCOUNT_CURSOR, null, this);说明会进行二次查询,可是此处查询的仍然是MailAppProvider缓存的结果,而非EmailProvider中的
 @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        switch (id) {
            case INIT_DRAFT_USING_REFERENCE_MESSAGE:
                return new CursorLoader(this, mRefMessageUri, UIProvider.MESSAGE_PROJECTION, null,
                        null, null);
            case REFERENCE_MESSAGE_LOADER:
                return new CursorLoader(this, mRefMessageUri, UIProvider.MESSAGE_PROJECTION, null,
                        null, null);
            case LOADER_ACCOUNT_CURSOR:

                return new CursorLoader(this, MailAppProvider.getAccountsUri(),
                        UIProvider.ACCOUNTS_PROJECTION, null, null, null);
        }
        return null;
    }

从上面的code可以看到uri和projection与AccountUtils.getAccounts一样,所以数据当然一样啊,仍然是INITIAL_SYNC_NEEDED。

8.那居然google原本就设计了第二次查询,那最简单的解决方法就是在二次查询的时候将uri指向EmailProvider这样,问题就解决了。
 @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        switch (id) {
            case INIT_DRAFT_USING_REFERENCE_MESSAGE:
                return new CursorLoader(this, mRefMessageUri, UIProvider.MESSAGE_PROJECTION, null,
                        null, null);
            case REFERENCE_MESSAGE_LOADER:
                return new CursorLoader(this, mRefMessageUri, UIProvider.MESSAGE_PROJECTION, null,
                        null, null);
            case LOADER_ACCOUNT_CURSOR:
                final String[] accountQueryUris = this.getResources().getStringArray(R.array.account_providers);
                return new CursorLoader(this, Uri.parse(accountQueryUris[0]),
                        UIProvider.ACCOUNTS_PROJECTION, null, null, null);
        }
        return null;
    }

此处指向的uri为content://com.android.email.provider/uiaccts

9.测试,问题解决。Google也有出错的时候,只要是人写的code就会有漏洞!
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics