天天看點

contacts 賬戶同步整體流程圖部分流程說明問題解決

本流程圖基于MTK平台 Android 7.0,本流程隻作為溝通學習使用

工作中遇到一個問題,手機登陸Google賬戶并同步賬戶裡面的contacts後,本地儲存的聯系人資料被無緣無故的删除了,甚是奇怪,通過對比前後的資料庫我們發現,同步後直接把我們的本地賬戶給删除了,是以導緻屬于這個賬戶的所有聯系人都不見了,那麼無緣無故為什麼會删除本地賬戶呢?我們先抛出問題,等我們看完整個流程再回來看看真是什麼。前提,我們的手機是被客制化過,不是android原生系統。

整體流程圖

contacts 賬戶同步整體流程圖部分流程說明問題解決

部分流程說明

我們重點看看updateAccountsInBackground這個方法

背景更新賬戶,判斷賬戶是否合法,不合法就删除合法就儲存

private boolean updateAccountsInBackground(Account[] systemAccounts) {
        if (!haveAccountsChanged(systemAccounts)) { //判斷目前賬戶是否有改變
            return false;
        }
.....
        Log.i(TAG, "Accounts changed");

        invalidateFastScrollingIndexCache();

        final ContactsDatabaseHelper dbHelper = mDbHelper.get();
        final SQLiteDatabase db = dbHelper.getWritableDatabase();
.....
        try {
            // First, remove stale rows from raw_contacts, groups, and related tables.

            // All accounts that are used in raw_contacts and/or groups.
            final Set<AccountWithDataSet> knownAccountsWithDataSets
                    = dbHelper.getAllAccountsWithDataSets();

            // Find the accounts that have been removed.
            final List<AccountWithDataSet> accountsWithDataSetsToDelete = Lists.newArrayList();
            for (AccountWithDataSet knownAccountWithDataSet : knownAccountsWithDataSets) {
                if (knownAccountWithDataSet.isLocalAccount()

                        || !canDeleteAccount(knownAccountWithDataSet)

                        || knownAccountWithDataSet.inSystemAccounts(systemAccounts, mSkipRemoval)) {
                    continue;
                }
                accountsWithDataSetsToDelete.add(knownAccountWithDataSet);//儲存需要删除的賬戶,後面如果這個list不為空就删除裡面賬戶的相關資訊
            }

            if (!accountsWithDataSetsToDelete.isEmpty()) {
                for (AccountWithDataSet accountWithDataSet : accountsWithDataSetsToDelete) {
.....做一些資料庫删除操作,把這個賬戶相關的資料都删除
              }
            }

            // Second, remove stale rows from Tables.SETTINGS and Tables.DIRECTORIES
            removeStaleAccountRows(
                    Tables.SETTINGS, Settings.ACCOUNT_NAME, Settings.ACCOUNT_TYPE, systemAccounts);
            removeStaleAccountRows(Tables.DIRECTORIES, Directory.ACCOUNT_NAME,
                    Directory.ACCOUNT_TYPE, systemAccounts);
.....一些删除操作
            /// @}

            // Third, remaining tasks that must be done in a transaction.
            // TODO: Should sync state take data set into consideration?
            dbHelper.getSyncState().onAccountsChanged(db, systemAccounts);
            saveAccounts(systemAccounts);//儲存新加的賬戶

            db.setTransactionSuccessful();
        } finally {
.....
        mAccountWritability.clear();

        updateContactsAccountCount(systemAccounts);
        updateProviderStatus();
        return true;
    }
           

haveAccountsChanged

檢視賬戶是否有變更

@VisibleForTesting
    boolean haveAccountsChanged(Account[] currentSystemAccounts) {
        final ContactsDatabaseHelper dbHelper = mDbHelper.get();
        final Set<Account> knownAccountSet;
        try {
            knownAccountSet =
                    stringToAccounts(dbHelper.getProperty(DbProperties.KNOWN_ACCOUNTS, ""));
        } catch (IllegalArgumentException e) {
            // Failed to get the last known accounts for an unknown reason.  Let's just
            // treat as if accounts have changed.
            return true;
        }
        final Set<Account> currentAccounts = Sets.newHashSet(currentSystemAccounts);
        return !knownAccountSet.equals(currentAccounts);
    }
           

isLocalAccount

通過賬戶類型判斷目前賬戶是否是本地賬戶

public static final String ACCOUNT_TYPE_LOCAL_PHONE = "Local Phone Account";
    private static final String ACCOUNT_TYPE_SIM = "SIM Account";
    private static final String ACCOUNT_TYPE_USIM = "USIM Account";
    private static final String ACCOUNT_TYPE_RUIM = "RUIM Account";
    private static final String ACCOUNT_TYPE_CSIM = "CSIM Account";
    private static final String LOCAL_ACCOUNT_TYPES = "('" +
            ACCOUNT_TYPE_LOCAL_PHONE + "' , '" +
            ACCOUNT_TYPE_SIM + "' , '" +
            ACCOUNT_TYPE_USIM + "' , '" +
            ACCOUNT_TYPE_RUIM + "' , '" +
            ACCOUNT_TYPE_CSIM + "')";
    /**
     * Check if the account is local account
     */
    public static boolean isLocalAccount(String accountType, String accountName) {
        return (accountType == null && accountName == null)
                || (accountType != null && LOCAL_ACCOUNT_TYPES.contains(accountType));
    }
           

inSystemAccounts

判斷目前賬戶是不是系統合法賬戶

public boolean inSystemAccounts(Account[] systemAccounts, boolean skip) {
        // Note we don't want to create a new Account object from this instance, as it may contain
        // null account name/type, which Account wouldn't accept.  So we need to compare field by
        // field.

        for (Account systemAccount : systemAccounts) {
            if (Objects.equal(systemAccount.name, getAccountName())//通過擷取賬戶名和賬戶類型來判斷
                    && Objects.equal(systemAccount.type, getAccountType())) {
                return true;
            }
        }
        return false;
    }
           

saveAccounts

将合法賬戶儲存下來并更新一些值

@VisibleForTesting
    void saveAccounts(Account[] systemAccounts) {
        final ContactsDatabaseHelper dbHelper = mDbHelper.get();
        dbHelper.setProperty(
                DbProperties.KNOWN_ACCOUNTS, accountsToString(Sets.newHashSet(systemAccounts)));
    }
           

問題解決

回顧我們的問題,我們是發現登陸賬戶同步聯系人後,本地賬戶被删除了,然而我們本地賬戶是被客制化了的,根據上面的流程我們可以知道,添加賬戶後會檢查已存在的賬戶和加入的賬戶是否是本地賬戶,并檢查賬戶是否合法,那麼我們就有懷疑點了,是否是我們的本地賬戶類型不合法呢?是以我們就有了解決辦法。

1.把我們預置的本地賬戶類型改成代碼中本地賬戶合法的類型

2.把我們預置的本地賬戶類型添加到LOCAL_ACCOUNT_TYPES 中,讓其成為一個合法的本地賬戶類型

繼續閱讀