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就会有漏洞!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值