AccountType相关数据结构
- AccountType. 就是我们所说的联系人存在什么地方,这里的AccountType可能是SIM, USIM, LOCAL PHONE,或者是我们的同步账户.它的子类有BaseAccountType.SimAccountType, UsimAccountType 刚才那个问题就是因为创建联系人使用的AccountType不同所以创建联系人的界面也不相同.
- DataKind. 与具体的某一项数据项(MIME)对应.比如说所有类型的电话号码是一个DataKind,所有类型的Email又是另外的一个DataKind.
三个主要的属性:
public int typeOverallMax; //表示这个DataKind有最多能有多少个数据项. -1为不限.
public AarryList<EditType> typeList; //所有该DataKind支持的EditType
public ArrayList<EditField> fieldList; //所有该DataKind支持的EditField
- EditType 就是一个数据项中的数据类型,对应联系人数据库data表的一行, 比如说phone number一项中的MobilePhone.
重要属性:public int rawValue; //标识某一数据项中的类型, 比如说MobilePhoneNumber 就是Phone.TYPE_MOBILE
public int rawValue; //标识某一数据项中的类型, 比如说MobilePhoneNumber 就是Phone.TYPE_MOBILE
public int lableRes; //该类型对应的输入框中的默认显示的名称 , 比如MobilePhoneNumber 就是com.android.internal.R.string.phoneTypeMobile;
public int specificMax; //表示该数据项最多能够被添加几次.默认值-1. 表示不限.
- EditField 就是一个数据项中的部分,对应联系人数据库 data表的一列, 同一个DataKind中的所有EditField将被写到同一行. 比如说Name中的FirstName
重要属性:
public String cloume; //表示着个EditField对应数据库data表中的哪一列 比如说FirstName对应data表的data2列
public int titleRes; //输入框中的默认显示的名称
public int inputType; //输入类型. EditorInfo.TYPE_CLASS_PHONE ....
从界面上看EditType和EditFiled的区别就是: EditField就是一个EditText, 而EditType是一个EditText在后面加了一个可以选类型的list.
在添加联系人是由于点击的AccountType不同, 那么创建出来的AccountType也不同,比如说SimAccountTypez只添加了Phone,Name,和Photo对应的DataKind所以只能添加姓名和手机号码.
EntityDelta 相关数据结构
EntityDelta表示的是一个UI上的操作对应在数据库中的所有操作, 例如:在UI上添加联系人, 那么EntityDelta会对应添加raw_contact,和data表中的数据。 EntityDelta主要有两个属性,private ValuesDelta mValues; 对应raw_contacts表的操作。raw_contact中的一行数据对应为一个联系人。 raw_contact主要包含了联系人的 类型(AccountType及账户数据) , 联系人的一些标志例如是否被收藏(star) ,是否被删除, 等等一些数据。 private HashMap<String, ArrayList<ValuesDelta>> mEntries对应数据库中的data表操作。 将MIME对应的数据操作放到同一个ArrayList中主要是包含了联系人的详细数据。 里面具体包含了哪些数据与联系人的AccountType相关。
ValueDelta是EntityDelta的一个内部内,主要用于保存数据,两个主要变量 protected ContentValues mBefore;protected ContentValues mAfter;用来存放数据。 然后通过mBefore和mAfter的状态来标识操作类型(修改,添加,删除)具体表现方式如下:
When "after" is present, then visible: isVisible
When "after" is wiped, action is "delete :isDelete
When no "before" or "after", is transient : isTransient
When "after" has some changes, action is "update" : isUpdate
When "after" has no changes, action is no-op:isNoop
When no "before" id, and has "after", action is "insert": isInsert
创建ValuesDelate的时候需要为其mAfter,mBefore赋值来标注不同的状态 创建ValuesDelta对象的主要方法:
public static ValuesDelta fromBefore(ContentValues before)
public static ValuesDelta fromAfter(ContentValues after)
fromBefore是将before赋值给mBefore 并且为mAfter创建一个ContentVlaues
fromAfter主要就是为insert和update创建ValuesDelta, 因为它将after赋值给mAfter 并且将mBefore赋值为null;
了解了EntityDelta 和ValueDelta,接下来我们看看EntityDelta是如何将保存在ContentValues中的数据,变为一条条数据库的操作的。 主要方法是
//这个方法是EntityDelta对象的方法, 它会调用ValueDelta中的buildDiff()为每一项数据创建操作
public void buildDiff(ArrayList<ContentProviderOperation> buildInto) {
final int firstIndex = buildInto.size();
//通过mValues数据来判断当前操作。
final boolean isContactInsert = mValues.isInsert();
final boolean isContactDelete = mValues.isDelete();
final boolean isContactUpdate = !isContactInsert && !isContactDelete;
final Long beforeId = mValues.getId();
Builder builder;
//如果是添加联系人就先将RawContacts.AGGREGATION_MODE设置为RawContacts.AGGREGATION_MODE_SUSPENDED,当数据保存完全后
//重新将值设置为RawContacts.AGGREGATION_MODE_DEFAULT
if (isContactInsert) {
// TODO: for now simply disabling aggregation when a new contact is
// created on the phone. In the future, will show aggregation suggestions
// after saving the contact.
mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED);
}
//调用ValuesDelta的buildDiff方法来创建raw_contacts表的操作
builder = mValues.buildDiff(mContactsQueryUri);
//将刚刚得到的raw_contacts的操作放入到操作列表中。
possibleAdd(buildInto, builder);
// 遍历所有的ValuesDelta
for (ArrayList<ValuesDelta> mimeEntries : mEntries.values()) {
for (ValuesDelta child : mimeEntries) {
// 如果是删除联系人就没必要操作data表了,因为删除raw_contact中数据时会自己删除data表对应数据
if (isContactDelete) continue;
// 如果数据是用户配置数据则使用profileURI。否则使用Data uri。//调用ValuesDelta进行创建数据库操作
if (mContactsQueryUri.equals(Profile.CONTENT_RAW_CONTACTS_URI)) {
builder = child.buildDiff(Uri.withAppendedPath(Profile.CONTENT_URI,
RawContacts.Data.CONTENT_DIRECTORY));
} else {
builder = child.buildDiff(Data.CONTENT_URI);
}
//如果子类型是插入
if (child.isInsert()) {
//如果整个操作是新增一个联系人
if (isContactInsert) {
// 添加联系人,需要在raw_contact表的数据创建之后才为子类型添加raw_contact_id。
builder.withValueBackReference(Data.RAW_CONTACT_ID, firstIndex);
} else {
// 子类型插入,整个操作为修改,则将之前的raw_contact_id赋值到子类型表中
builder.withValue(Data.RAW_CONTACT_ID, beforeId);
}
} else if (isContactInsert && builder != null) {
// 如果整个操作为添加联系人,子类型却不是新增则报错
throw new IllegalArgumentException("When parent insert, child must be also");
}
//再次将builder中的数据操作放入到整个操作的列表中
possibleAdd(buildInto, builder);
}
}
final boolean addedOperations = buildInto.size() > firstIndex;
if (addedOperations && isContactUpdate) {
//为联系人添加新的数据,首先先将RawContacts.AGGREGATION_MODE置为AGGREGATION_MODE_SUSPENDED,将新的数据放入后重新置为AGGREGATION_MODE_DEFAULT
builder = buildSetAggregationMode(beforeId, RawContacts.AGGREGATION_MODE_SUSPENDED);
buildInto.add(firstIndex, builder.build());
builder = buildSetAggregationMode(beforeId, RawContacts.AGGREGATION_MODE_DEFAULT);
buildInto.add(builder.build());
} else if (isContactInsert) {
//如果是插入联系人,则在所有数据操作都添加到列表中后重新将RawContacts.AGGREGATION_MODE置为AGGREGATION_MODE_DEFAULT
builder = ContentProviderOperation.newUpdate(mContactsQueryUri);
builder.withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DEFAULT);
builder.withSelection(RawContacts._ID + "=?", new String[1]);
builder.withSelectionBackReference(0, firstIndex);
buildInto.add(builder.build());
}
}
//创建一个数据的操作
public ContentProviderOperation.Builder buildDiff(Uri targetUri) {
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;
}