联系人聚合ContactAggregator2分析
16lz
2022-07-24
AbstractContactAggregator的子类有ContactAggregator2和ContactAggregator,目前版本使用ContactAggregator2。
packages/providers/ContactsProvider/src/com/android/providers/contacts/aggregation/ContactAggregator2.java
findMatchingContacts
protected List findMatchingContacts(final SQLiteDatabase db, long contactId, ArrayList parameters) { MatchCandidateList candidates = new MatchCandidateList(); RawContactMatcher matcher = new RawContactMatcher(); if (parameters == null || parameters.size() == 0) { final Cursor c = db.query(RawContactIdQuery.TABLE, RawContactIdQuery.COLUMNS, RawContacts.CONTACT_ID + "=" + contactId, null, null, null, null); try { while (c.moveToNext()) { long rawContactId = c.getLong(RawContactIdQuery.RAW_CONTACT_ID); long accountId = c.getLong(RawContactIdQuery.ACCOUNT_ID); // Don't aggregate a contact with its own raw contacts. matcher.keepOut(rawContactId, contactId, accountId); updateMatchScoresForSuggestionsBasedOnDataMatches(db, rawContactId, candidates, matcher); } } finally { c.close(); } } else { updateMatchScoresForSuggestionsBasedOnDataMatches(db, candidates, matcher, parameters); } return matcher.pickBestMatches(SCORE_THRESHOLD_SUGGEST); }
抽象方法实现之一 private void updateMatchScoresForSuggestionsBasedOnDataMatches(SQLiteDatabase db, long rawContactId, MatchCandidateList candidates, RawContactMatcher matcher) { updateMatchScoresBasedOnIdentityMatch(db, rawContactId, matcher); updateMatchScoresBasedOnNameMatches(db, rawContactId, matcher); updateMatchScoresBasedOnEmailMatches(db, rawContactId, matcher); updateMatchScoresBasedOnPhoneMatches(db, rawContactId, matcher); loadNameMatchCandidates(db, rawContactId, candidates, false); //依据RawContacts获取候选人 lookupApproximateNameMatches(db, candidates, matcher); //更新MatchScore}private void updateMatchScoresForSuggestionsBasedOnDataMatches(SQLiteDatabase db, MatchCandidateList candidates, RawContactMatcher matcher, ArrayList parameters) { for (AggregationSuggestionParameter parameter : parameters) { if (AggregationSuggestions.PARAMETER_MATCH_NAME.equals(parameter.kind)) { updateMatchScoresBasedOnNameMatches(db, parameter.value, candidates, matcher); } }}
updateMatchScoresForSuggestionsBasedOnDataMatches方法有两个版本,带用户参数的版本最终会使用 private void updateMatchScoresBasedOnNameMatches(SQLiteDatabase db, String query, MatchCandidateList candidates, RawContactMatcher matcher) //依据candidates更新matcher
而不带用户参数版本会依据Data表中的各个MIME类型数据更新MatchScore,然后还依据近似名字更新MatchScore private void lookupApproximateNameMatches(SQLiteDatabase db, MatchCandidateList candidates, RawContactMatcher matcher) ////注意算法是RawContactMatcher.MATCHING_ALGORITHM_APPROXIMATE
private void matchAllCandidates(SQLiteDatabase db, String selection, MatchCandidateList candidates, RawContactMatcher matcher, int algorithm, String limit) //该方法只被lookupApproximateNameMatches调用
更新MatchScore方法
private void updateMatchScoresBasedOnExceptions(SQLiteDatabase db, long rawContactId, RawContactMatcher matcher)//被updateMatchScores使用,依据rawContactId在Tables.AGGREGATION_EXCEPTIONS中查找聚合候选人并加入matcher中private void updateMatchScoresBasedOnIdentityMatch(SQLiteDatabase db, long rawContactId, RawContactMatcher matcher) //类似updateMatchScoresBasedOnExceptions,依据是IDENTITYprivate void updateMatchScoresBasedOnNameMatches(SQLiteDatabase db, long rawContactId, RawContactMatcher matcher) //类似updateMatchScoresBasedOnExceptions,依据是名字private void updateMatchScoresBasedOnEmailMatches(SQLiteDatabase db, long rawContactId, RawContactMatcher matcher) //类似updateMatchScoresBasedOnExceptions,依据是电子邮件地址private void updateMatchScoresBasedOnPhoneMatches(SQLiteDatabase db, long rawContactId, RawContactMatcher matcher)//类似updateMatchScoresBasedOnExceptions,依据是号码private void updateScoreForCandidatesWithoutName(SQLiteDatabase db, List secondaryRawContactIds, RawContactMatcher matcher) //联系人无名字情况的处理。private void updateMatchScores(SQLiteDatabase db, long rawContactId, MatchCandidateList candidates, RawContactMatcher matcher) { //使用上述多个方法更新MatchScore //update primary score updateMatchScoresBasedOnExceptions(db, rawContactId, matcher); updateMatchScoresBasedOnNameMatches(db, rawContactId, matcher); // update scores only if the raw contact doesn't have structured name if (rawContactWithoutName(db, rawContactId)) { updateMatchScoresBasedOnIdentityMatch(db, rawContactId, matcher); updateMatchScoresBasedOnEmailMatches(db, rawContactId, matcher); updateMatchScoresBasedOnPhoneMatches(db, rawContactId, matcher); final List secondaryRawContactIds = matcher.prepareSecondaryMatchCandidates(); if (secondaryRawContactIds != null && secondaryRawContactIds.size() <= SECONDARY_HIT_LIMIT) { updateScoreForCandidatesWithoutName(db, secondaryRawContactIds, matcher); } }}
其中rawContactWithoutName如下: private boolean rawContactWithoutName(SQLiteDatabase db, long rawContactId) //被updateMatchScores使用,只有没有名字的时候才会进行其他数据的匹配
aggregateContact
抽象方法之二synchronized void aggregateContact(TransactionContext txContext, SQLiteDatabase db, long rawContactId, long accountId, long currentContactId, MatchCandidateList candidates) { if (!needAggregate(db, rawContactId)) { //判断是否要聚合,否的话直接return return; } int aggregationMode = RawContacts.AGGREGATION_MODE_DEFAULT; Integer aggModeObject = mRawContactsMarkedForAggregation.remove(rawContactId); if (aggModeObject != null) { aggregationMode = aggModeObject; //获取聚合模式 } RawContactMatcher matcher = new RawContactMatcher(); RawContactMatchingCandidates matchingCandidates = new RawContactMatchingCandidates(); if (aggregationMode == RawContacts.AGGREGATION_MODE_DEFAULT) { // If this is a newly inserted contact or a visible contact, look for // data matches. if (currentContactId == 0 //是新插入的联系人(id为0)或者是可见联系人 || mDbHelper.isContactInDefaultDirectory(db, currentContactId)) { // Find the set of matching candidates matchingCandidates = findRawContactMatchingCandidates(db, rawContactId, candidates, matcher); //找到聚合候选联系人列表 } } else if (aggregationMode == RawContacts.AGGREGATION_MODE_DISABLED) { return; //聚合模式是DISABLED,返回 } long currentContactContentsCount = 0; if (currentContactId != 0) { mRawContactCountQuery.bindLong(1, currentContactId); mRawContactCountQuery.bindLong(2, rawContactId); currentContactContentsCount = mRawContactCountQuery.simpleQueryForLong(); } //属于currentContactId的RawContact Id数量-1(排除自己),这个是目前Contact相关的RawContact数量-1 final int operation; final int candidatesCount = matchingCandidates.getCount(); //要合并的RawContact数量 if (candidatesCount >= AGGREGATION_CONTACT_SIZE_LIMIT) { operation = KEEP_INTACT; //数量大于50没法合并 } else if (candidatesCount > 0) { operation = RE_AGGREGATE; //要聚合 } else { // When there is no matching raw contact found, if there are no other raw contacts in // the current aggregate, we might as well reuse it. Also, if the aggregation mode is // SUSPENDED, we must reuse the same aggregate. if (currentContactId != 0 && (currentContactContentsCount == 0 || aggregationMode == RawContacts.AGGREGATION_MODE_SUSPENDED)) { operation = KEEP_INTACT; //已有联系人并且无需合并,啥也不做 } else { operation = CREATE_NEW_CONTACT; //其它情况,新建联系人,注意currentContactId是为0则是普通的新建联系人,如果不为0那么实际是联系人拆分 } } if (operation == KEEP_INTACT) { // Aggregation unchanged markAggregated(db, String.valueOf(rawContactId)); //直接标记合并完毕 } else if (operation == CREATE_NEW_CONTACT) { // create new contact for [rawContactId] createContactForRawContacts(db, txContext, Sets.newHashSet(rawContactId), null); //创建新联系人 if (currentContactContentsCount > 0) { //如果当前Contact下有其它的RawContact,那么要重新更新数据,因为当前的RawContact已经属于新的Contact了 updateAggregateData(txContext, currentContactId); } markAggregated(db, String.valueOf(rawContactId)); //标记合并完毕 } else { //候选联系人数量大于0的时候会走这里,真正的合并 // re-aggregate reAggregateRawContacts(txContext, db, currentContactId, rawContactId, accountId, currentContactContentsCount, matchingCandidates); } }
needAggregate private boolean needAggregate(SQLiteDatabase db, long rawContactId) //aggregateContact中判断是否需要聚合,就是判断RawContactsColumns.AGGREGATION_NEEDED是否为1
findRawContactMatchingCandidates private RawContactMatchingCandidates findRawContactMatchingCandidates(SQLiteDatabase db, long rawContactId, MatchCandidateList candidates, RawContactMatcher matcher) //使用updateMatchScores来获取候选联系人列表
reAggregateRawContacts
正真合并的方法
private void reAggregateRawContacts(TransactionContext txContext, SQLiteDatabase db, long currentCidForRawContact, long rawContactId, long accountId, long currentContactContentsCount, RawContactMatchingCandidates matchingCandidates) { final Set allIds = new HashSet<>(); allIds.add(rawContactId); allIds.addAll(matchingCandidates.getRawContactIdSet()); final Set> connectedRawContactSets = findConnectedRawContacts(db, allIds); //关于AbstractContactAggregator的文章中已经介绍过这个方法, //connectedRawContactSets中的每个Set都是有关联的RawContact id集合,会合并为1个Contact final Map rawContactsToAccounts = matchingCandidates.getRawContactToAccount(); rawContactsToAccounts.put(rawContactId, accountId); ContactAggregatorHelper.mergeComponentsWithDisjointAccounts(connectedRawContactSets, rawContactsToAccounts); //connectedRawContactSets中的每个元素中有可能有多个账户的数据,一个账户的数据也可能在多个元素中, //并且每个元素代表一个维度的共同点,例如某个元素是靠email关联的,另外一个是靠号码关联的。这样的话//将账户数据只在唯一一个元素中的这些元素合并是安全的(这几个元素只是同一联系人不同账户角度下的数据), //而同一个账户数据在不同元素意味这这几个元素是不同的联系人可能性很大。 //详细见mergeComponentsWithDisjointAccounts代码 breakComponentsByExceptions(db, connectedRawContactSets); //将合并策略为TYPE_KEEP_SEPARATE的都分离出去,不用合并。分离出去的每个Set只有一个元素 // Create new contact for each connected component. Use the first reusable contactId if // possible. If no reusable contactId found, create new contact for the connected component. // Update aggregate data for all the contactIds touched by this connected component, for (Set connectedRawContactIds : connectedRawContactSets) { //针对connectedRawContactSets循环 Long contactId = null; Set cidsNeedToBeUpdated = new HashSet<>(); if (connectedRawContactIds.contains(rawContactId)) { //候选RawContact Id中包含当前的rawContactId,那么表示可以重用当前rawContactId所属的Contact而不用新建联系人 // If there is no other raw contacts aggregated with the given raw contact currently // or all the raw contacts in [currentCidForRawContact] are still in the same // connected component, we might as well reuse it. if (currentCidForRawContact != 0 && (currentContactContentsCount == 0) || canBeReused(db, currentCidForRawContact, connectedRawContactIds)) { //可以重用currentCidForRawContact contactId = currentCidForRawContact; for (Long connectedRawContactId : connectedRawContactIds) { Long cid = matchingCandidates.getContactId(connectedRawContactId); if (cid != null && !cid.equals(contactId)) { cidsNeedToBeUpdated.add(cid); //RawContact在聚合后所属Contact会有变化,当前这些Contact id标记后续更新 } } } else if (currentCidForRawContact != 0){ //不能使用当前Contact Id, 标记 cidsNeedToBeUpdated.add(currentCidForRawContact); } } else { boolean foundContactId = false; for (Long connectedRawContactId : connectedRawContactIds) { Long currentContactId = matchingCandidates.getContactId(connectedRawContactId); if (!foundContactId && currentContactId != null && canBeReused(db, currentContactId, connectedRawContactIds)) { //从候选RawContact id列表中选择第一个可用的Contact Id contactId = currentContactId; foundContactId = true; } else { //其它的Contact Id标记 cidsNeedToBeUpdated.add(currentContactId); } } } final String connectedRids = TextUtils.join(",", connectedRawContactIds); //依据connectedRawContactIds生成用逗号分隔Contact Id的字符串 clearSuperPrimarySetting(db, connectedRids); //对Data.IS_SUPER_PRIMARY做处理,合并后的同类型Data数据只可能有一个row的Data.IS_SUPER_PRIMARY为1 createContactForRawContacts(db, txContext, connectedRawContactIds, contactId); //生成或者更新联系人,contactId不为null就是更新,为null是新建 markAggregated(db, connectedRids); //标记合并完毕 for (Long cid : cidsNeedToBeUpdated) { long currentRcCount = 0; //获取到归属于该Contact id的所有RawContact数量 if (cid != 0) { mRawContactCountQuery.bindLong(1, cid); mRawContactCountQuery.bindLong(2, 0); currentRcCount = mRawContactCountQuery.simpleQueryForLong(); } if (currentRcCount == 0) { //如果数量等于0,则直接删除该Contact // Delete a contact if it doesn't contain anything ContactsTableUtil.deleteContact(db, cid); mAggregatedPresenceDelete.bindLong(1, cid); mAggregatedPresenceDelete.execute(); } else { //否则更新该Contacts updateAggregateData(txContext, cid); } } } }
其中用的的几个方法: private void clearSuperPrimarySetting(SQLiteDatabase db, String rawContactIds) //给定rawContact中如果Data.IS_SUPER_PRIMARY为1且row数量大于1,设置对应Data.IS_SUPER_PRIMARY为0,因为IS_SUPER_PRIMARY的不可能在同一Contact的多个RawContact数据中值大于0,reAggregateRawContacts中使用private String buildExceptionMatchingSql(String rawContactIdSet1, String rawContactIdSet2, int aggregationType, boolean countOnly) //查询Tables.AGGREGATION_EXCEPTIONS的语句,返回规则countprivate void breakComponentsByExceptions(SQLiteDatabase db, Set> connectedRawContacts)//把所有聚合策略是AggregationExceptions.TYPE_KEEP_SEPARATE的从connectedRawContacts中分离并重新加入,表示不再会参加后续聚合,reAggregateRawContacts使用它private boolean canBeReused(SQLiteDatabase db, Long contactId, Set connectedRawContactIds) //判断connectedRawContactIds是否能涵盖contactId旗下的RawContactId,是的话contactId可以重用,聚合后的联系人使用该id,否则要重新建立一个Contact,reAggregateRawContacts使用它
其它
最后一个抽象方法的实现public void updateAggregationAfterVisibilityChange(long contactId) //实现的基类抽象方法,联系人可见有变化的时候重新走聚合流程
更多相关文章
- 怎么去掉联系人、通话记录、拨号列表界面中的电话号码中间的空格
- 【contacts】Phonebook电话本
- android 获取手机中的联系人
- 转:Android之通信录中的联系人操作
- 获取android联系人信息
- Android之通信录中的联系人操作
- android分页查询获取系统联系人信息
- android联系人过滤
- Android(安卓)学习笔记 Contacts (一)ContentResolver query 参数