2014-01-07 11:18:08 将百度空间里的东西移过来。
1. Save contact
我们前面已经写了四篇文章,做了大量的铺垫,总算到了这一步,见证奇迹的时刻终于到了。
用户添加了所有需要添加的信息后,点击“Done”来保存新建好的联系人,我们就从用户点击“Done”Button开始分析。
前面提到过,“Done”的处理事件是在ContactEditorActivity里面设置的,如下:
1 View saveMenuItem = customActionBarView.findViewById(R.id.save_menu_item); 2 saveMenuItem.setOnClickListener(new OnClickListener() { 3 @Override 4 public void onClick(View v) { 5 mFragment.doSaveAction(); 6 } 7 });
我们进入ContactEditorFragment,看他的调用逻辑,doSaveAction()-->save(SaveMode.CLOSE),重点看save()方法:
1 public boolean save(int saveMode) { 2 ... 3 // Save contact 4 Intent intent = ContactSaveService.createSaveContactIntent( 5 mContext, mState, 6 SAVE_MODE_EXTRA_KEY, saveMode, isEditingUserProfile(), 7 getActivity().getClass(), 8 ContactEditorActivity.ACTION_SAVE_COMPLETED, 9 mUpdatedPhotos, mPbrIndex); 10 mContext.startService(intent); 11 }
可以看到,我们千里追踪的mState被当作参数,和其他对象一样,进入了ContactSaveService,这个ContactSaveService继承自IntentService,进入ContactSaveService之后调用了一个回调方法,如下:
1 @Override 2 protected void onHandleIntent(Intent intent) { 3 String action = intent.getAction(); 4 if (ACTION_SAVE_CONTACT.equals(action)) { 5 saveContact(intent); 6 CallerInfoCacheUtils.sendUpdateCallerInfoCacheIntent(this); 7 }
看saveContact(intent),这个方法就是保存联系人的终极方法:
1 private void saveContact(Intent intent) { 2 RawContactDeltaList state = intent.getParcelableExtra(EXTRA_CONTACT_STATE); 3 final boolean isProfile = intent.getBooleanExtra(EXTRA_SAVE_IS_PROFILE, false); 4 Bundle updatedPhotos = intent.getParcelableExtra(EXTRA_UPDATED_PHOTOS); 5 6 final AccountTypeManager accountTypes = AccountTypeManager.getInstance(this); 7 8 String sourceId = null; 9 boolean isNewAdnContact = false; 10 RawContactDelta entityDelta = state.get(0); 11 final AccountType type = entityDelta.getAccountType(accountTypes); 12 13 RawContactModifier.trimEmpty(state, accountTypes, isProfile); 14 15 Uri lookupUri = null; 16 17 final ContentResolver resolver = getContentResolver(); 18 boolean succeeded = false; 19 20 long insertedRawContactId = -1; 21 22 int tries = 0; 23 while (tries++ < PERSIST_TRIES) { 24 final ArrayList<ContentProviderOperation> diff = state.buildDiff(); 25 26 ContentProviderResult[] results = null; 27 if (!diff.isEmpty()) { 28 results = resolver.applyBatch(ContactsContract.AUTHORITY, diff); 29 } 30 final long rawContactId = getRawContactId(state, diff, results); 31 if (rawContactId == -1) { 32 } 33 insertedRawContactId = getInsertedRawContactId(diff, results); 34 final Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 35 lookupUri = RawContacts.getContactLookupUri(resolver, rawContactUri); 36 succeeded = true; 37 38 if (isAdnContact && isNewAdnContact && lookupUri != null) { 39 long contactId = ContentUris.parseId(lookupUri); 40 long newRawContactId = getRawContactId(state, diff, results); 41 AdnContactsCollector.updateAdnContactsNow( 42 contactId, newRawContactId, 43 entityDelta.getAccountType(), 44 entityDelta.getAccountName(), sourceId); 45 } 46 break; 47 } 48 reportSaveStatus(intent, lookupUri, succeeded); 49 }
上面的方法并非是完整的方法,我只是截取了重点代码,看while循环,说明会try保存联系人三次,而不是一次。我们前面一致关注的state = intent.getParcelableExtra(EXTRA_CONTACT_STATE);同时还从intent中取出了账户信息等,而最关键的代码实在while循环里面,首先看final ArrayList<ContentProviderOperation> diff = state.buildDiff();我们知道,保存联系人时一般会把所有的信息封装到ContentProviderOperation里面(可以参考http://www.cnblogs.com/wlrhnh/p/3477216.html 和 http://www.cnblogs.com/wlrhnh/p/3477252.html),然后执行resolver.applyBatch,那么现在的问题就是如何封装ContentProviderOperation了,我们知道state是RawContactDeltaList,而且根据前面的分析,它里面添加了一个RawContactDelta对象,下面我们进入RawContactDeltaList的state.buildDiff()方法:
1 public ArrayList<ContentProviderOperation> buildDiff() { 2 final ArrayList<ContentProviderOperation> diff = Lists.newArrayList(); 3 4 for (RawContactDelta delta : this) { 5 final int firstBatch = diff.size(); 6 final boolean isInsert = delta.isContactInsert(); 7 backRefs[rawContactIndex++] = isInsert ? firstBatch : -1; 8 9 delta.buildDiff(diff); 10 11 if (mIsUnlinkingRawContact) continue; 12 13 if (rawContactId != -1) { 14 final Builder builder = beginKeepTogether(); 15 builder.withValue(AggregationExceptions.RAW_CONTACT_ID1, rawContactId); 16 builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID2, firstBatch); 17 diff.add(builder.build()); 18 19 } else if (firstInsertRow == -1) { 20 firstInsertRow = firstBatch; 21 22 } else { 23 final Builder builder = beginKeepTogether(); 24 builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID1, firstInsertRow); 25 builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID2, firstBatch); 26 diff.add(builder.build()); 27 } 28 } 29 return diff; 30 }
首先for循环中取出了RawContactDelta对象,一般情况下只有一个,然后调用delta.buildDiff(diff),看来还得进入RawContactDelta的buildDiff()方法,此处传入的diff是一个ArrayList<ContentProviderOperation>对象:
1 for (ArrayList<ValuesDelta> mimeEntries : mEntries.values()) { 2 for (ValuesDelta child : mimeEntries) { 3 if (mContactsQueryUri.equals(Profile.CONTENT_RAW_CONTACTS_URI)) { 4 builder = child.buildDiff(Uri.withAppendedPath(Profile.CONTENT_URI, RawContacts.Data.CONTENT_DIRECTORY)); 5 } else { 6 builder = child.buildDiff(Data.CONTENT_URI); 7 } 8 } 9 }
看外层的for循环,mEntries是HashMap<String, ArrayList<ValuesDelta>>对象,应该还记得前文中分析的saveValue(String column, String value)方法吧,如下:
1 protected void saveValue(String column, String value) { 2 Log.d("David", "column = " + column); 3 Log.d("David", "value = " + value); 4 mEntry.put(column, value); 5 Log.d("David", "mState = " + mState); 6 }
其中mEntry是ValuesDelta对象,而且有ValuesDelta entry : mState.getMimeEntries(mKind.mimeType)在KindSectionView.java。
继续分析内层循环,取出每一个ValuesDelta对象,然后调用child.buildDiff(Data.CONTENT_URI),进入ValuesDelta:
1 public ContentProviderOperation.Builder buildDiff(Uri targetUri) { 2 Builder builder = null; 3 if (isInsert()) { 4 mAfter.remove(mIdColumn); 5 builder = ContentProviderOperation.newInsert(targetUri); 6 builder.withValues(mAfter); 7 } 8 return builder; 9 }
好了,我们看这几个循环,我在每一循环下面都打了log:
1 RawContactDeltaList: for (RawContactDelta delta : this) 2 Log.d("D1", "delta = " + delta); 3 4 RawContactDelta: for (ArrayList<ValuesDelta> mimeEntries : mEntries.values()) 5 Log.d("D1", "==================================="); 6 Log.d("D1", "mimeEntries = " + mimeEntries); 7 8 RawContactDelta: for (ValuesDelta child : mimeEntries) 9 Log.d("D1", "child = " + child); 10 11 ValuesDelta: buildDiff(Uri targetUri) 12 Log.d("D1", "mAfter = " + mAfter);
当我输入Name=Lucky, PhoneNumber=18611112222,然后点击保存联系人时,log结果如下:
RawContactDelta只有一个,包含所有用户输入的信息,可见RawContactDelta是一个包含所有联系人信息的对象;
每一个RawContactDelta都包含好几个ArrayList<ValuesDelta>,其实每一个ArrayList只有一个对象ValuesDelta;那么一个ValuesDelta包含一个Item的信息,如Name, Phone, Email。然后每一个ValuesDelta做buildDiff()操作,builder.withValues(mAfter),mAfter是一个ContentValues对象,打印结果如上图mAfter所示。
好了,现在返回到最开始的位置,ContactSaveService.java,saveContact()方法,有
1 final ArrayList<ContentProviderOperation> diff = state.buildDiff(); 2 3 ContentProviderResult[] results = null; 4 if (!diff.isEmpty()) { 5 results = resolver.applyBatch(ContactsContract.AUTHORITY, diff); 6 }
resolver.applyBatch操作,那我们看看最终返回的diff是什么?
1 for (ContentProviderOperation d : diff) { 2 Log.d("D2", "ContentProviderOperation = " + d); 3 } 4 results = resolver.applyBatch(ContactsContract.AUTHORITY, diff);
log截图如下:
可以看到每一个ContentProviderOperation中都附带相应的值,而这些值会被resolver.applyBatch()方法保存在数据库里。
至此,新建联系人UI和保存联系人分析结束。