联系人聚合AbstractContactAggregator分析

AbstractContactAggregator

packages/providers/ContactsProvider/src/com/android/providers/contacts/aggregation/AbstractContactAggregator.java

这个是聚合相关类中的最长文件了,有2000多行。从名字就能看出是抽象类,实现类有ContactAggregator2和ContactAggregator,它们联系人聚合的核心类。

成员

sql语句

 protected SQLiteStatement mRawContactCountQuery;
    protected SQLiteStatement mAggregatedPresenceDelete;
    protected SQLiteStatement mAggregatedPresenceReplace;
    protected SQLiteStatement mPresenceContactIdUpdate;
    protected SQLiteStatement mMarkForAggregation;
    protected SQLiteStatement mPhotoIdUpdate;
    protected SQLiteStatement mDisplayNameUpdate;
    protected SQLiteStatement mLookupKeyUpdate;
    protected SQLiteStatement mStarredUpdate;
    protected SQLiteStatement mPinnedUpdate;
    protected SQLiteStatement mContactIdAndMarkAggregatedUpdate;
    protected SQLiteStatement mContactIdUpdate;
    protected SQLiteStatement mContactUpdate;
    protected SQLiteStatement mContactInsert;
    protected SQLiteStatement mResetPinnedForRawContact;
这些sql语句在构造方法中初始化

public AbstractContactAggregator(ContactsProvider2 contactsProvider,
            ContactsDatabaseHelper contactsDatabaseHelper,
            PhotoPriorityResolver photoPriorityResolver, NameSplitter nameSplitter,
            CommonNicknameCache commonNicknameCache) {
        mContactsProvider = contactsProvider;
        mDbHelper = contactsDatabaseHelper;
        mPhotoPriorityResolver = photoPriorityResolver;
        mNameSplitter = nameSplitter;
        mCommonNicknameCache = commonNicknameCache;

        SQLiteDatabase db = mDbHelper.getReadableDatabase();

        // Since we have no way of determining which custom status was set last,
        // we'll just pick one randomly.  We are using MAX as an approximation of randomness
        final String replaceAggregatePresenceSql =
                "INSERT OR REPLACE INTO " + Tables.AGGREGATED_PRESENCE + "("
                + AggregatedPresenceColumns.CONTACT_ID + ", "
                + StatusUpdates.PRESENCE + ", "
                + StatusUpdates.CHAT_CAPABILITY + ")"
                + " SELECT " + PresenceColumns.CONTACT_ID + ","
                + StatusUpdates.PRESENCE + ","
                + StatusUpdates.CHAT_CAPABILITY
                + " FROM " + Tables.PRESENCE
                + " WHERE "
                + " (" + StatusUpdates.PRESENCE
                +       " * 10 + " + StatusUpdates.CHAT_CAPABILITY + ")"
                + " = (SELECT "
                + "MAX (" + StatusUpdates.PRESENCE
                +       " * 10 + " + StatusUpdates.CHAT_CAPABILITY + ")"
                + " FROM " + Tables.PRESENCE
                + " WHERE " + PresenceColumns.CONTACT_ID
                + "=?)"
                + " AND " + PresenceColumns.CONTACT_ID
                + "=?;";
        mAggregatedPresenceReplace = db.compileStatement(replaceAggregatePresenceSql);
        //依据Contact id更新AGGREGATED_PRESENCE表,updateAggregateData方法中使用

        mRawContactCountQuery = db.compileStatement(
                "SELECT COUNT(" + RawContacts._ID + ")" +
                " FROM " + Tables.RAW_CONTACTS +
                " WHERE " + RawContacts.CONTACT_ID + "=?"
                        + " AND " + RawContacts._ID + "<>?"); 
        //依据Contact id 和RawContact id查询RawContact数量,aggregateContact和reAggregateRawContacts方法中使用

        mAggregatedPresenceDelete = db.compileStatement(
                "DELETE FROM " + Tables.AGGREGATED_PRESENCE +
                " WHERE " + AggregatedPresenceColumns.CONTACT_ID + "=?"); 
        //依据Contact id删除AGGREGATED_PRESENCE表,reAggregateRawContacts方法中使用

        mMarkForAggregation = db.compileStatement(
                "UPDATE " + Tables.RAW_CONTACTS +
                " SET " + RawContactsColumns.AGGREGATION_NEEDED + "=1" +
                " WHERE " + RawContacts._ID + "=?"
                        + " AND " + RawContactsColumns.AGGREGATION_NEEDED + "=0"); 
        //依据RawContact id更新RAW_CONTACTS表,把RawContactsColumns.AGGREGATION_NEEDED设置为1,触发后续的聚合

        mPhotoIdUpdate = db.compileStatement(
                "UPDATE " + Tables.CONTACTS +
                " SET " + Contacts.PHOTO_ID + "=?," + Contacts.PHOTO_FILE_ID + "=? " +
                " WHERE " + Contacts._ID + "=?");
        //依据Contact id更新CONTACTS表的photo相关字段,updatePhotoId方法中被调用,进而又被DataRowHandlerForPhoto中的增删改调用

        mDisplayNameUpdate = db.compileStatement(
                "UPDATE " + Tables.CONTACTS +
                " SET " + Contacts.NAME_RAW_CONTACT_ID + "=? " +
                " WHERE " + Contacts._ID + "=?");
        //依据Contact id设置CONTACTS表的RawContact Id,updateDisplayNameForContact方法中调用

        mLookupKeyUpdate = db.compileStatement(
                "UPDATE " + Tables.CONTACTS +
                " SET " + Contacts.LOOKUP_KEY + "=? " +
                " WHERE " + Contacts._ID + "=?");
        //依据Contact id更新CONTACTS表的LOOKUP_KEY,updateLookupKeyForRawContact和updateDisplayNameForContact方法中调用

        mStarredUpdate = db.compileStatement("UPDATE " + Tables.CONTACTS + " SET "
                + Contacts.STARRED + "=(SELECT (CASE WHEN COUNT(" + RawContacts.STARRED
                + ")=0 THEN 0 ELSE 1 END) FROM " + Tables.RAW_CONTACTS + " WHERE "
                + RawContacts.CONTACT_ID + "=" + ContactsColumns.CONCRETE_ID + " AND "
                + RawContacts.STARRED + "=1)" + " WHERE " + Contacts._ID + "=?");
        //依据Contact id更新CONTACTS表的星标字段,updateRawContact和updateRawContactsStar方法中调用

        mPinnedUpdate = db.compileStatement("UPDATE " + Tables.CONTACTS + " SET "
                + Contacts.PINNED + " = IFNULL((SELECT MIN(" + RawContacts.PINNED + ") FROM "
                + Tables.RAW_CONTACTS + " WHERE " + RawContacts.CONTACT_ID + "="
                + ContactsColumns.CONCRETE_ID + " AND " + RawContacts.PINNED + ">"
                + PinnedPositions.UNPINNED + ")," + PinnedPositions.UNPINNED + ") "
                + "WHERE " + Contacts._ID + "=?");
        //依据Contact id更新CONTACTS表的PINNED字段,updateRawContact方法中调用

        mContactIdAndMarkAggregatedUpdate = db.compileStatement(
                "UPDATE " + Tables.RAW_CONTACTS +
                " SET " + RawContacts.CONTACT_ID + "=?, "
                        + RawContactsColumns.AGGREGATION_NEEDED + "=0" +
                " WHERE " + RawContacts._ID + "=?");
        //依据RawContact id更新RAW_CONTACTS表的CONTACT_ID和AGGREGATION_NEEDED字段,createContactForRawContacts方法中使用

        mContactIdUpdate = db.compileStatement(
                "UPDATE " + Tables.RAW_CONTACTS +
                " SET " + RawContacts.CONTACT_ID + "=?" +
                " WHERE " + RawContacts._ID + "=?");
        //依据RawContact id更新RAW_CONTACTS表的CONTACT_ID字段,这个在Raw_Contacts表中插入数据后调用

        mPresenceContactIdUpdate = db.compileStatement(
                "UPDATE " + Tables.PRESENCE +
                " SET " + PresenceColumns.CONTACT_ID + "=?" +
                " WHERE " + PresenceColumns.RAW_CONTACT_ID + "=?");
        //依据RawContact id更新PRESENCE表的CONTACT_ID字段,这个代码调用的地方和mContactIdAndMarkAggregatedUpdate是在一起的

        mContactUpdate = db.compileStatement(ContactReplaceSqlStatement.UPDATE_SQL); //更新Contacts表,updateAggregateData方法中使用
        mContactInsert = db.compileStatement(ContactReplaceSqlStatement.INSERT_SQL); //插入Contacts表,插入RawContact数据或者在reAggregateRawContacts方法中使用

        mResetPinnedForRawContact = db.compileStatement( //解除pinned状态,代码中没有地方使用这个语句
                "UPDATE " + Tables.RAW_CONTACTS +
                " SET " + RawContacts.PINNED + "=" + PinnedPositions.UNPINNED +
                " WHERE " + RawContacts._ID + "=?"); 
        ...
    }

NameMatchCandidate和MatchCandidateList

    protected static class NameMatchCandidate {
        String mName; 
        int mLookupType;

        public NameMatchCandidate(String name, int nameLookupType) {
            mName = name;
            mLookupType = nameLookupType;
        }
    }
两个成员,名字和类型。类型见 联系人存储ContactsProvider表分析中NAME_LOOKUP小节分析
protected static class MatchCandidateList {
        protected final ArrayList<NameMatchCandidate> mList = new ArrayList<NameMatchCandidate>();
        protected int mCount;
        ...
    }
NameMatchCandidate的列表
    protected MatchCandidateList mCandidates = new MatchCandidateList();

DisplayNameCandidate

   private static class DisplayNameCandidate {
        long rawContactId;
        String displayName;
        int displayNameSource;
        boolean isNameSuperPrimary;
        boolean writableAccount;

        ...
    }
    protected DisplayNameCandidate mDisplayNameCandidate = new DisplayNameCandidate();
这个是更新名字字段时候用到。

其它

要聚合的RawContact Id集合结构,其中key是id,value是聚合模式:

protected HashMap<Long, Integer> mRawContactsMarkedForAggregation = Maps.newHashMap();
聚合规则表中的RawContact Id集合
protected final HashSet<Long> mAggregationExceptionIds = new HashSet<Long>();

在prefetchAggregationExceptionIdsfan方法中初始化了mAggregationExceptionIds 

外界触发聚合的方法

aggregateInTransaction

 public void aggregateInTransaction(TransactionContext txContext, SQLiteDatabase db) {
        final int markedCount = mRawContactsMarkedForAggregation.size();
        //代码走到这里的时候mRawContactsMarkedForAggregation是填充好的
        int index = 0;

        final StringBuilder sbQuery = new StringBuilder();
        sbQuery.append(AggregationQuery.SQL);
        for (long rawContactId : mRawContactsMarkedForAggregation.keySet()) {
            if (index > 0) {
                sbQuery.append(',');
            }
            sbQuery.append(rawContactId);
            index++;
        }

        sbQuery.append(')'); //mRawContactsMarkedForAggregation中的id作为查询条件

        final long[] rawContactIds;
        final long[] contactIds;
        final long[] accountIds;
        final int actualCount;
        final Cursor c = db.rawQuery(sbQuery.toString(), null);
        try {
            actualCount = c.getCount();
            rawContactIds = new long[actualCount];
            contactIds = new long[actualCount];
            accountIds = new long[actualCount];

            index = 0;
            while (c.moveToNext()) {
                rawContactIds[index] = c.getLong(AggregationQuery._ID);
                contactIds[index] = c.getLong(AggregationQuery.CONTACT_ID);
                accountIds[index] = c.getLong(AggregationQuery.ACCOUNT_ID);
                index++;
            }
        } finally {
            c.close();
        }

        for (int i = 0; i < actualCount; i++) {
            aggregateContact(txContext, db, rawContactIds[i], accountIds[i], contactIds[i],
                    mCandidates);  //循环针每个RawContact处理
        }

    }
aggregateInTransaction在联系人事务完毕后调用,见ContactsProvider2中

    protected void onCommitTransactionInternal(boolean forProfile) {
        ...
        mAggregator.get().aggregateInTransaction(mTransactionContext.get(), db);
        ...
    }

对事务处理过的数据进行合并处理。例如批量导入vcard或者卡联系人都是通过事务进行操作的。

triggerAggregation

public final void triggerAggregation(TransactionContext txContext, long rawContactId) {
        ...
        int aggregationMode = mDbHelper.getAggregationMode(rawContactId); //获取聚合模式,依据模式分别处理
        switch (aggregationMode) {
            case RawContacts.AGGREGATION_MODE_DISABLED: //禁用情况下什么也不做
                break;

            case RawContacts.AGGREGATION_MODE_DEFAULT: {
                markForAggregation(rawContactId, aggregationMode, false); //标记该rawContact
                break;
            }

            case RawContacts.AGGREGATION_MODE_SUSPENDED: { 
                long contactId = mDbHelper.getContactId(rawContactId);

                if (contactId != 0) {
                    updateAggregateData(txContext, contactId); //更新Contacts表相关数据
                }
                break;
            }

            case RawContacts.AGGREGATION_MODE_IMMEDIATE: {
                aggregateContact(txContext, mDbHelper.getWritableDatabase(), rawContactId); //聚合联系人
                break;
            }
        }
    }
triggerAggregation被packages/providers/ContactsProvider/src/com/android/providers/contacts/DataRowHandler.java中的方法调用
    public void triggerAggregation(TransactionContext txContext, long rawContactId) {
        mContactAggregator.triggerAggregation(txContext, rawContactId);
    }
DataRowHandler有很多子类,是针对各种MIMETYPE的DATA数据,所以Data表有变动即会触发联系人的聚合流程

标记方法

下面的几个方法标记要聚合的联系人
    public final void markNewForAggregation(long rawContactId, int aggregationMode) {
        mRawContactsMarkedForAggregation.put(rawContactId, aggregationMode);
    }

    public final void markForAggregation(long rawContactId, int aggregationMode, boolean force) {
        final int effectiveAggregationMode;
        if (!force && mRawContactsMarkedForAggregation.containsKey(rawContactId)) {
            // As per ContactsContract documentation, default aggregation mode
            // does not override a previously set mode
            if (aggregationMode == RawContacts.AGGREGATION_MODE_DEFAULT) { //默认模式不更改之前设置的模式
                effectiveAggregationMode = mRawContactsMarkedForAggregation.get(rawContactId);
            } else {
                effectiveAggregationMode = aggregationMode;
            }
        } else {
            mMarkForAggregation.bindLong(1, rawContactId);
            mMarkForAggregation.execute(); //更改Raw_Contacts表中的AGGREGATION_NEEDED字段为1,标记为后续要聚合
            effectiveAggregationMode = aggregationMode;
        }

        mRawContactsMarkedForAggregation.put(rawContactId, effectiveAggregationMode);
    }

    protected final void markContactForAggregation(SQLiteDatabase db, long contactId)
    //传入的是Contact Id,所以它的作用是依据Contact Id找到RawContact Id, 然后循环调用markForAggregation

    public final int markAllVisibleForAggregation(SQLiteDatabase db)
    //它会更新RawContact表中AGGREGATION_NEEDED字段为1,条件是在默认DIRECTORY且聚合模式为默认,想手动触发整合全部联系人的话,这个方法好用。

    protected final void markAggregated(SQLiteDatabase db, String rawContactIds)
    //设置AGGREGATION_NEEDED为0,标记聚合结束

    public abstract void updateAggregationAfterVisibilityChange(long contactId);//抽象方法,不过子类实现就是标记要聚合

聚合方法

aggregateContact

    public void aggregateContact(
            TransactionContext txContext, SQLiteDatabase db, long rawContactId) 
    //该方法是调用同名的另外一个方法

    abstract void aggregateContact(TransactionContext txContext, SQLiteDatabase db,
            long rawContactId, long accountId, long currentContactId,
            MatchCandidateList candidates); 
    //该方法子类中实现
最核心的方法在子类实现。

updateAggregateData

    public void updateAggregateData(TransactionContext txContext, long contactId) {

        ...
        computeAggregateData(db, contactId, mContactUpdate); //获取要更新的数据
        mContactUpdate.bindLong(ContactReplaceSqlStatement.CONTACT_ID, contactId);
        mContactUpdate.execute(); //更新Contacts表
        ...
    }

    protected void computeAggregateData(SQLiteDatabase db, long contactId,
            SQLiteStatement statement) {
        mSelectionArgs1[0] = String.valueOf(contactId);
        computeAggregateData(db, mRawContactsQueryByContactId, mSelectionArgs1, statement);
    } 
    //mRawContactsQueryByContactId是sql语句,实际是RawContact、Data和ACCOUNT三个表的联合

    protected final void computeAggregateData(final SQLiteDatabase db, String sql, String[] sqlArgs,
            SQLiteStatement statement) 
    //生产聚合联系人要填充或更新的数据,基本逻辑是个循环,从cursor读取各个字段,确认最佳的字段值。
computeAggregateData方法比较长,是核心的实现。
    protected void appendLookupKey(StringBuilder sb, String accountTypeWithDataSet,
            String accountName, long rawContactId, String sourceId, String displayName)  //computeAggregateData使用,生成lookupKey
    private void processDisplayNameCandidate(long rawContactId, String displayName,
            int displayNameSource, boolean writableAccount, boolean isNameSuperPrimary)  //computeAggregateData使用,生成名字
    private PhotoEntry getPhotoMetadata(SQLiteDatabase db, long photoFileId)   // computeAggregateData使用,生成照片
上述是computeAggregateData中使用的三个方法

createContactForRawContacts

    protected final void d(SQLiteDatabase db,
            TransactionContext txContext, Set<Long> rawContactIds, Long contactId) 
如果contactId为null则新建一个联系人然后更新数据,否者就直接更新数据,这个就是聚合联系人流程最后要走的步骤。


寻找聚合联系人子集的方法

    protected final String buildIdentityMatchingSql(String rawContactIdSet1,
            String rawContactIdSet2, boolean isIdentityMatching, boolean countOnly)  
    protected final String buildEmailMatchingSql(String rawContactIdSet1, String rawContactIdSet2,
            boolean countOnly)
    protected final String buildPhoneMatchingSql(String rawContactIdSet1, String rawContactIdSet2,
            boolean countOnly)
    protected final String buildExceptionMatchingSql(String rawContactIdSet1,
            String rawContactIdSet2)
    protected final Set<Set<Long>> findConnectedRawContacts(SQLiteDatabase db, Set<Long>
            rawContactIdSet) { 
        //给定RawContact Id集合,使用上述四个方法寻找其中有关联的子集,有点像数据结构课中给定图找强联通分量。每一个子集会合并成一个联系人
        // Connections between two raw contacts
        final Multimap<Long, Long> matchingRawIdPairs = HashMultimap.create();
        String rawContactIds = TextUtils.join(",", rawContactIdSet);
        findIdPairs(db, buildExceptionMatchingSql(rawContactIds, rawContactIds),
                matchingRawIdPairs);
        findIdPairs(db, buildIdentityMatchingSql(rawContactIds, rawContactIds,
                /* isIdentityMatching =*/ true, /* countOnly =*/false), matchingRawIdPairs);
        findIdPairs(db, buildEmailMatchingSql(rawContactIds, rawContactIds, /* countOnly =*/false),
                matchingRawIdPairs);
        findIdPairs(db, buildPhoneMatchingSql(rawContactIds, rawContactIds,  /* countOnly =*/false),
                matchingRawIdPairs);

        return ContactAggregatorHelper.findConnectedComponents(rawContactIdSet, matchingRawIdPairs);
    }
前四个个方法是生成sql查询字符串的,目的是已有两个RawContact Id集合,寻找他们之间的关联,前三个分别依据身份Identity,邮件地址,电话号码,最后一个特殊,是依据用户设置的规则(例如用户在联系人列表中手动选择了多条联系人然后点击合并菜单项就是往AGGREGATION_EXCEPTIONS中表插入了数据然后再触发合并) 

获取匹配联系人列表的方法

    protected final void loadNameMatchCandidates(SQLiteDatabase db, long rawContactId,
            MatchCandidateList candidates, boolean structuredNameBased)
    //获取名字匹配的候选联系人列表

    protected final void updateMatchScoresBasedOnNameMatches(SQLiteDatabase db, String query,
            MatchCandidateList candidates, ContactMatcher matcher) 
    //对候选联系人一一进行评分,评分见之前介绍的ContactMatcher类

    private void matchAllCandidates(SQLiteDatabase db, String selection,
            MatchCandidateList candidates, ContactMatcher matcher, int algorithm, String limit) 
    //这个方法没有使用

更新相关方法

    public final void updatePhotoId(SQLiteDatabase db, long rawContactId)     //更新Contacts表中photoid
    public final void updateDisplayNameForRawContact(SQLiteDatabase db, long rawContactId) 
    public final void updateDisplayNameForContact(SQLiteDatabase db, long contactId)     //更新Contacts表中名字
    public final void updateHasPhoneNumber(SQLiteDatabase db, long rawContactId)     //更新Contacts表中HAS_PHONE_NUMBER字段
    public final void updateLookupKeyForRawContact(SQLiteDatabase db, long rawContactId) //更新RawContacts表中LOOKUP_KEY字段
    private void updateLookupKeyForContact(SQLiteDatabase db, long contactId)     //更新Contacts表中LOOKUP_KEY字段
    protected String computeLookupKeyForContact(SQLiteDatabase db, long contactId)  //计算LOOKUP_KEY的值
    public final void updateStarred(long rawContactId) //更新联系人星标值
    public final void updatePinned(long rawContactId) //更新PINNED字段
更新Contacts表单个字段的方法,大部分很简单,基本都是本文开头介绍sql语句的执行。

查询相关方法

    public final Cursor queryAggregationSuggestions(SQLiteQueryBuilder qb,
            String[] projection, long contactId, int maxSuggestions, String filter,
            ArrayList<AggregationSuggestionParameter> parameters) 

    private Cursor queryMatchingContacts(SQLiteQueryBuilder qb, SQLiteDatabase db,
            String[] projection, List<MatchScore> bestMatches, int maxSuggestions, String filter)     

    protected abstract List<MatchScore> findMatchingContacts(final SQLiteDatabase db,
            long contactId, ArrayList<AggregationSuggestionParameter> parameters);  //子类实现
供聚合查询用的两个函数,会返回推荐合并的联系人数据,对应于ContactsProvider2中的两个Uri
        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
                AGGREGATION_SUGGESTIONS);
        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
                AGGREGATION_SUGGESTIONS);




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值