MTK Contacts代码分析

1.本文主要记录Contacts代码中和数据有关的类之间的关系,从中可以看到编辑Contact界面是如何与Contact数据联系在一起的。
RawContactDeltaList本质是ArrayList,用add来向里面添加RawContactDelta类型的对象。
在新建一个联系人的时候,先new一个RawContact,RawContact的构造函数如下:

Screenshot from 2017-07-14 15:03:11.png

然后,会把新建联系人选择的Account信息加入到RawContact中,也就是在RawContact的mValues中put ACCOUNT_NAME、ACCOUNT_TYPE、DATA_SET信息,如LocalPhoneAccount对应的AccountWithDataSet为(“Phone”,“Local Phone Account”,null)
------------------到现在为止,新建一个RawContact对象,并在里面防止Account信息---------------------
接下来调用ValuesDelta的fromAfter放,传入的参数是上面新建的RawContact对象的mValues变量,fromAfter方法如下:

Screenshot from 2017-07-14 15:12:17.png

接下来,新建一个RawContactDelta对象,构造函数中传入的参数是上面的ValuesDelta对象。构造函数如下:

Screenshot from 2017-07-14 15:14:38.png

接下来会调用,RawContactModifier.parseExtras(mContext, accountType, result, mIntentExtras),其中的result就是上面新建的RawContactDelta,这个函数的第一步是调用parseStructuredNameExtra,而parseStructuredNameExtra函数第一步调用的是RawContactModifier.ensureKindExists(state, accountType, StructuredName.CONTENT_ITEM_TYPE);
参数state是RawContactDelta,accountType是LocalPhoneAccountType,这个函数的含义就是先判断此种Account是否必须加入此种mimeType,如果必须加入此种mimeType,那么就从RawContactDelta中去看看是否有这种Entry,注意RawContactDelta中有mValues和mEntries两个不同的变量,如果没有的话就要调用insertChild了,这个函数就是新建ContentValues,然后put进一些数据,然后调用ValuesDelta.fromAfter,然后将得到的ValuesDelta addEntry到RawContactDelta中,insertChild函数如下:

Screenshot from 2017-07-14 15:48:48.png

-------------------------到这里应该可以看出来,RawContactDelta中的mValues保存的是此次插入联系人的Account信息,mEntries是这次插入联系人对应的AccountType应该有的DataKind对应的ValuesDelta--------------------------------------------------------------------------------------------------------------
之后会调用CompactRawContactsEditorView的setState方法,在这个setState方法里面会调用parseRawContactDeltas方法,通过阅读代码可以看到这个方法的作用是将RawContactDeltaList解析到Map<String,KindSectionDataList> mKindSectionDataMap中,一个mimetype对应一个KindSectionDataList,KindSectionDataList保存的是KindSectionData对象,new KindSectionData需要accountType, dataKind, rawContactDelta。之后获得mPrimaryNameKindSectionData,这个变量就是StructuredName这个mimetype对应的KindSectionData和ValuesDelta对应的Pair。然后会调用addKindSectionViews方法,这个方法就是根据解析到的mKindSectionDataMap数据来addView。以StructuredName为例,inflateKindSectionView方法inflate一个CompactKindSectionView,之后调用CompactKindSectionView的setState方法,传入的两个重要的参数就是StructuredName对应的KindSectionData和ValuesDelta,然后调用addNameEditorViews创建Name的界面,这个addNameEditorViews方法会调用inflate一个StructuredNameEditorView,然后调用StructuredNameEditorView的setValues,把ValuesDelta和RawContactDelta传进去,StructuredNameEditorView extends TextFieldsEditorView在这个TextFieldsEditorView里面的setValues方法中,当fieldView内容发生变化时,会调用onFieldChanged方法,然后调用saveValue方法,函数调用如下:

Screenshot from 2017-07-14 17:57:31.png

这个mEntry就是ValuesDelta,也就是说当界面的内容发生变化后,会使ValuesDelta也发生变化。这个ValuesDelta也是在RawContactDelta中的mEntries里面。

2.在ContactSaveService的saveContact分析。
最简单的新建一个联系人,只有一个名字和号码。RawContactDelta的buildDiffWrapper结果出来总共有四个operation,分别是insert raw_contact,insert data,insert data,update raw_contact。insert raw_contact属于RawContactDelta的mValues数据,里面有Account信息,insert data,insert data属于RawContactDelta的mEntries,也就是编辑联系人界面输入的联系人信息。但是,raw_contact和data是通过raw_contact_id关联起来的。由于在buildDiffWrapper的时候并没有插入raw_contact表,那么插入data表的时候是如何保证raw_contact_id正确的呢?在RawContactDelta的buildDiffWrapper中,解析完一个ValuesDelta后,会有这么一句代码:bw.getBuilder().withValueBackReference(Data.RAW_CONTACT_ID, firstIndex);//firsetIndex为0.
四个operation是通过ContactsProvider2的applyBatch得到执行的,按照顺序先是insert raw_contact,于是调用到了operation的apply方法:results[i] = operation.apply(this, results, i)。可以看到,insert raw_contact后,会把results[0]赋值为new ContentProviderResult(newUri),这个newUri就是insert raw_contact返回的uri。之后insert data,执行operation的apply方法的时候,会先解析下mValuesBackReferences,其实就是把results[0]里的newUri找出id部分,然后把他放到ContentValues中,这样就把raw_contact_id的正确值找到了,这就是operation和withValueBackReference的用途之一。

3.联系人搜索
还是以最简单的新建一个联系人为例
插入联系人会解析RawContactDelta,也就是用buildDiffWrapper方法来解析RawContactDelta的mValues和mEntries变量(都是ValuesDelta对象),buildDiffWrapper会先调用buildDiffHelper,buildDiffHelper代码如下:
private ContentProviderOperation.Builder buildDiffHelper(Uri targetUri) {
ContentProviderOperation.Builder builder = null;
if (isInsert()) {
// Changed values are "insert" back-referenced to Contact
mAfter.remove(mIdColumn);
builder = ContentProviderOperation.newInsert(targetUri);
builder.withValues(mAfter);
} else if (isDelete()) {
// When marked for deletion and "before" exists, then "delete"
builder = ContentProviderOperation.newDelete(targetUri);
builder.withSelection(mIdColumn + "=" + getId(), null);
} else if (isUpdate()) {
// When has changes and "before" exists, then "update"
builder = ContentProviderOperation.newUpdate(targetUri);
builder.withSelection(mIdColumn + "=" + getId(), null);
builder.withValues(mAfter);
}
return builder;
}
也就是new一个ContentProviderOperation.Builder对象,设置下Builder的mValues变量,buildDiffWrapper代码如下:
public BuilderWrapper buildDiffWrapper(Uri targetUri) {
final ContentProviderOperation.Builder builder = buildDiffHelper(targetUri);
BuilderWrapper bw = null;
if (isInsert()) {
bw = new BuilderWrapper(builder, CompatUtils.TYPE_INSERT);
} else if (isDelete()) {
bw = new BuilderWrapper(builder, CompatUtils.TYPE_DELETE);
} else if (isUpdate()) {
bw = new BuilderWrapper(builder, CompatUtils.TYPE_UPDATE);
}
android.util.Log.i("sela","buildDiffWrapper targetUri="+targetUri
+",mimeType="+getMimetype()+",type="+((bw != null) ?bw.getType():null));
return bw;
}
也就是将前面new的builder封装到BuilderWrapper中。

会生成四个CPOWrapper(即ContentProviderOperationWrapper),类如下:
public class CPOWrapper {
private ContentProviderOperation mOperation;
private int mType;

public CPOWrapper(ContentProviderOperation builder, int type) {
    mOperation = builder;
    mType = type;
}

public int getType() {
    return mType;
}

public void setType(int type) {
    this.mType = type;
}

public ContentProviderOperation getOperation() {
    return mOperation;
}

public void setOperation(ContentProviderOperation operation) {
    this.mOperation = operation;
}

}
这四个CPOWrapper:
第一个uri是content://com.android.contacts/raw_contacts,ContentValues是account_type=Local Phone Account aggregation_mode=2 account_name=Phone data_set=null------>调用ContactsProvider2的insertRawContact方法
第二个uri是content://com.android.contacts/data,ContentValues是raw_contact_id=398 data1=(884) 879-94 data2=2 mimetype=vnd.android.cursor.item/phone_v2------>调用ContactsProvider2的insertData方法
第三个uri是content://com.android.contacts/data,ContentValues是raw_contact_id=398 data5=悟 data1=孙悟空 data2=空 data6=null data4=null data3=孙 is_super_primary=1 mimetype=vnd.android.cursor.item/name
第四个uri是content://com.android.contacts/raw_contacts,ContentValues是aggregation_mode=0------>调用ContactsProvider2的updateRawContact方法
insertData方法会去调用DataRowHandler的insert方法如DataRowHandlerForStructuredName的insert会插入到data表,然后由于我们是数据库的transaction,当所有的operation执行完毕会调用onCommit方法,然后会调用updateRawContactDisplayName方法,主要是根据data表的名字来更新raw_contact相关字段,然后有onRawContactInsert方法主要是根据raw_contacts表中的内容来新建一个contacts表记录,最后是通过SearchIndexManager的updateIndexForRawContacts方法来更新search_index表,用来联系人搜索用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341

推荐阅读更多精彩内容