1.本文主要記錄Contacts代碼中和數(shù)據(jù)有關(guān)的類之間的關(guān)系,從中可以看到編輯Contact界面是如何與Contact數(shù)據(jù)聯(lián)系在一起的腥椒。
RawContactDeltaList本質(zhì)是ArrayList,用add來向里面添加RawContactDelta類型的對(duì)象耻瑟。
在新建一個(gè)聯(lián)系人的時(shí)候庞溜,先new一個(gè)RawContact破讨,RawContact的構(gòu)造函數(shù)如下:
然后,會(huì)把新建聯(lián)系人選擇的Account信息加入到RawContact中尔苦,也就是在RawContact的mValues中put ACCOUNT_NAME涩馆、ACCOUNT_TYPE、DATA_SET信息允坚,如LocalPhoneAccount對(duì)應(yīng)的AccountWithDataSet為(“Phone”,“Local Phone Account”,null)
------------------到現(xiàn)在為止魂那,新建一個(gè)RawContact對(duì)象,并在里面防止Account信息---------------------
接下來調(diào)用ValuesDelta的fromAfter放稠项,傳入的參數(shù)是上面新建的RawContact對(duì)象的mValues變量涯雅,fromAfter方法如下:
接下來,新建一個(gè)RawContactDelta對(duì)象展运,構(gòu)造函數(shù)中傳入的參數(shù)是上面的ValuesDelta對(duì)象活逆。構(gòu)造函數(shù)如下:
接下來會(huì)調(diào)用,RawContactModifier.parseExtras(mContext, accountType, result, mIntentExtras)拗胜,其中的result就是上面新建的RawContactDelta蔗候,這個(gè)函數(shù)的第一步是調(diào)用parseStructuredNameExtra,而parseStructuredNameExtra函數(shù)第一步調(diào)用的是RawContactModifier.ensureKindExists(state, accountType, StructuredName.CONTENT_ITEM_TYPE);
參數(shù)state是RawContactDelta,accountType是LocalPhoneAccountType,這個(gè)函數(shù)的含義就是先判斷此種Account是否必須加入此種mimeType埂软,如果必須加入此種mimeType锈遥,那么就從RawContactDelta中去看看是否有這種Entry,注意RawContactDelta中有mValues和mEntries兩個(gè)不同的變量仰美,如果沒有的話就要調(diào)用insertChild了迷殿,這個(gè)函數(shù)就是新建ContentValues,然后put進(jìn)一些數(shù)據(jù)咖杂,然后調(diào)用ValuesDelta.fromAfter庆寺,然后將得到的ValuesDelta addEntry到RawContactDelta中,insertChild函數(shù)如下:
-------------------------到這里應(yīng)該可以看出來诉字,RawContactDelta中的mValues保存的是此次插入聯(lián)系人的Account信息懦尝,mEntries是這次插入聯(lián)系人對(duì)應(yīng)的AccountType應(yīng)該有的DataKind對(duì)應(yīng)的ValuesDelta--------------------------------------------------------------------------------------------------------------
之后會(huì)調(diào)用CompactRawContactsEditorView的setState方法,在這個(gè)setState方法里面會(huì)調(diào)用parseRawContactDeltas方法壤圃,通過閱讀代碼可以看到這個(gè)方法的作用是將RawContactDeltaList解析到Map<String,KindSectionDataList> mKindSectionDataMap中陵霉,一個(gè)mimetype對(duì)應(yīng)一個(gè)KindSectionDataList,KindSectionDataList保存的是KindSectionData對(duì)象伍绳,new KindSectionData需要accountType, dataKind, rawContactDelta踊挠。之后獲得mPrimaryNameKindSectionData,這個(gè)變量就是StructuredName這個(gè)mimetype對(duì)應(yīng)的KindSectionData和ValuesDelta對(duì)應(yīng)的Pair。然后會(huì)調(diào)用addKindSectionViews方法效床,這個(gè)方法就是根據(jù)解析到的mKindSectionDataMap數(shù)據(jù)來addView睹酌。以StructuredName為例,inflateKindSectionView方法inflate一個(gè)CompactKindSectionView,之后調(diào)用CompactKindSectionView的setState方法剩檀,傳入的兩個(gè)重要的參數(shù)就是StructuredName對(duì)應(yīng)的KindSectionData和ValuesDelta憋沿,然后調(diào)用addNameEditorViews創(chuàng)建Name的界面,這個(gè)addNameEditorViews方法會(huì)調(diào)用inflate一個(gè)StructuredNameEditorView沪猴,然后調(diào)用StructuredNameEditorView的setValues辐啄,把ValuesDelta和RawContactDelta傳進(jìn)去,StructuredNameEditorView extends TextFieldsEditorView在這個(gè)TextFieldsEditorView里面的setValues方法中运嗜,當(dāng)fieldView內(nèi)容發(fā)生變化時(shí)壶辜,會(huì)調(diào)用onFieldChanged方法,然后調(diào)用saveValue方法洗出,函數(shù)調(diào)用如下:
這個(gè)mEntry就是ValuesDelta士复,也就是說當(dāng)界面的內(nèi)容發(fā)生變化后,會(huì)使ValuesDelta也發(fā)生變化翩活。這個(gè)ValuesDelta也是在RawContactDelta中的mEntries里面。
2.在ContactSaveService的saveContact分析便贵。
最簡(jiǎn)單的新建一個(gè)聯(lián)系人菠镇,只有一個(gè)名字和號(hào)碼。RawContactDelta的buildDiffWrapper結(jié)果出來總共有四個(gè)operation承璃,分別是insert raw_contact,insert data,insert data,update raw_contact利耍。insert raw_contact屬于RawContactDelta的mValues數(shù)據(jù),里面有Account信息,insert data,insert data屬于RawContactDelta的mEntries盔粹,也就是編輯聯(lián)系人界面輸入的聯(lián)系人信息隘梨。但是,raw_contact和data是通過raw_contact_id關(guān)聯(lián)起來的舷嗡。由于在buildDiffWrapper的時(shí)候并沒有插入raw_contact表轴猎,那么插入data表的時(shí)候是如何保證raw_contact_id正確的呢?在RawContactDelta的buildDiffWrapper中进萄,解析完一個(gè)ValuesDelta后捻脖,會(huì)有這么一句代碼:bw.getBuilder().withValueBackReference(Data.RAW_CONTACT_ID, firstIndex);//firsetIndex為0.
四個(gè)operation是通過ContactsProvider2的applyBatch得到執(zhí)行的,按照順序先是insert raw_contact中鼠,于是調(diào)用到了operation的apply方法:results[i] = operation.apply(this, results, i)可婶。可以看到援雇,insert raw_contact后矛渴,會(huì)把results[0]賦值為new ContentProviderResult(newUri),這個(gè)newUri就是insert raw_contact返回的uri惫搏。之后insert data具温,執(zhí)行operation的apply方法的時(shí)候蚕涤,會(huì)先解析下mValuesBackReferences,其實(shí)就是把results[0]里的newUri找出id部分桂躏,然后把他放到ContentValues中钻趋,這樣就把raw_contact_id的正確值找到了,這就是operation和withValueBackReference的用途之一剂习。
3.聯(lián)系人搜索
還是以最簡(jiǎn)單的新建一個(gè)聯(lián)系人為例
插入聯(lián)系人會(huì)解析RawContactDelta蛮位,也就是用buildDiffWrapper方法來解析RawContactDelta的mValues和mEntries變量(都是ValuesDelta對(duì)象),buildDiffWrapper會(huì)先調(diào)用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一個(gè)ContentProviderOperation.Builder對(duì)象失仁,設(shè)置下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中们何。
會(huì)生成四個(gè)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;
}
}
這四個(gè)CPOWrapper:
第一個(gè)uri是content://com.android.contacts/raw_contacts,ContentValues是account_type=Local Phone Account aggregation_mode=2 account_name=Phone data_set=null------>調(diào)用ContactsProvider2的insertRawContact方法
第二個(gè)uri是content://com.android.contacts/data冤竹,ContentValues是raw_contact_id=398 data1=(884) 879-94 data2=2 mimetype=vnd.android.cursor.item/phone_v2------>調(diào)用ContactsProvider2的insertData方法
第三個(gè)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
第四個(gè)uri是content://com.android.contacts/raw_contacts,ContentValues是aggregation_mode=0------>調(diào)用ContactsProvider2的updateRawContact方法
insertData方法會(huì)去調(diào)用DataRowHandler的insert方法如DataRowHandlerForStructuredName的insert會(huì)插入到data表,然后由于我們是數(shù)據(jù)庫(kù)的transaction鹦蠕,當(dāng)所有的operation執(zhí)行完畢會(huì)調(diào)用onCommit方法冒签,然后會(huì)調(diào)用updateRawContactDisplayName方法,主要是根據(jù)data表的名字來更新raw_contact相關(guān)字段钟病,然后有onRawContactInsert方法主要是根據(jù)raw_contacts表中的內(nèi)容來新建一個(gè)contacts表記錄萧恕,最后是通過SearchIndexManager的updateIndexForRawContacts方法來更新search_index表,用來聯(lián)系人搜索用肠阱。