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 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表,用来联系人搜索用。