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和保存联系人分析结束。

转载于:https://www.cnblogs.com/wlrhnh/p/3508481.html

Android Phonebook编写联系人UI加载及联系人保存流程(六)相关推荐

  1. Android框架之路——Glide加载图片(结合RecyclerView、CardView)

    Android框架之路--Glide加载图片 一.简介: 在泰国举行的谷歌开发者论坛上,谷歌为我们介绍了一个名叫 Glide 的图片加载库,作者是bumptech.这个库被广泛的运用在google的开 ...

  2. android多种方式实现异步加载图片

    记得之前做安卓应用时都是在2.2以下的版本,如果在UI线程中进行耗时操作,比如http,socket等 会产生android.os.NetworkOnMainThreadException 如果异步加 ...

  3. Carson带你学Android:主流开源图片加载库对比(UIL、Picasso、Glide、Fresco)

    前言 图片加载在 Android开发项目中十分常见 为了降低开发周期 & 难度,我们经常会选用一些图片加载的开源库,而现在图片加载开源库越来越多,我们应该选用哪种呢? 今天.我就给大家介绍 & ...

  4. android 仿 动画,Android动画 - 仿58同城加载动画

    Android动画 - 仿58同城加载动画 效果图 58LoadingView.gif 分析动画 首先分析动画,如上图所示: 动画分为三部分,分别为上方跳动部分,中间阴影部分,和下方文字部分. 上方跳 ...

  5. Android 中LayoutInflater(布局加载器)之介绍篇

    本文出自博客Vander丶CSDN博客,如需转载请标明出处,尊重原创谢谢 博客地址:http://blog.csdn.net/l540675759/article/details/78099358 前 ...

  6. 使用 Picasso 加载显示联系人头像

    Picasso 自带了对加载显示联系人头像的支持,只要传入联系人头像的 URL,例如 "photo:content://com.android.contacts/contacts/1005/ ...

  7. android编程中添加gif,Android应用开发之【Android】使用android-gif-drawable包加载GIF动图...

    本文将带你了解Android应用开发之[Android]使用android-gif-drawable包加载GIF动图,希望本文对大家学Android有所帮助. [导包] 首先需要导入android-g ...

  8. 【Android 安全】DEX 加密 ( 不同 Android 版本的 DEX 加载 | Android 8.0 版本 DEX 加载分析 | Android 5.0 版本 DEX 加载分析 )

    文章目录 一.不同版本的 DEX 加载 1.Android 8.0 版本 DEX 加载分析 2.Android 6.0 版本 DEX 加载分析 3.Android 5.0 版本 DEX 加载分析 一. ...

  9. 剖析Picasso加载压缩本地图片流程(解决Android 5.0部分机型无法加载本地图片的问题)

    之前项目中使用Picasso遇到了一个问题:在Android 5.0以上版本的部分手机上使用Picasso加载本地图片会失败.为了解决这个问题,研究了一下Picasso加载和压缩本地图片的流程,才有了 ...

最新文章

  1. 排序算法-------堆排序
  2. 创建总账科目类型会计凭证
  3. 前端学习(2750):global全局外观设置
  4. [转]使用T4模板批量生成代码
  5. 网络流24题-骑士共存问题
  6. codeforces590b//Chip 'n Dale Rescue Rangers//Codeforces Round #327 (Div. 1)
  7. Java设计模式之四 ----- 适配器模式和桥接模式
  8. Matlab采用梯度算子、拉普拉斯算子、Sobel算子及Prewitt算子对图像进行锐化
  9. WordPress个人博客Cosy3.1.3主题+积木部分插件
  10. 计算机组成原理学习笔记(四)指令系统(学习王道)
  11. 构建自己的 LINUX 系统(二)
  12. Ubuntu完全教程
  13. 在C++与python间传视频帧
  14. Tomcat网站服务
  15. [Servlet] HttpServletRequest
  16. 2022新版加壳工具-支持.NET虚拟化加密
  17. 从与迪思杰签约 看浪潮主机生态如何布局?
  18. 闭关修炼(十一)网络通信
  19. 算法试题——每日一练
  20. 使用 tree 命令生成目录

热门文章

  1. Python Logging.basicConfig
  2. windows下安装HTK3.4
  3. 停止、启动或重新启动 VMware vCenter Server Appliance 6.x 及更高版本上的服务 (2109887)
  4. 测试助手健康 Test-AssistantHealth failed for server
  5. Linux的文件传输工具(WinSCP)付下载连接
  6. 技术分享——机房搬迁工作步骤及方案详解
  7. Java 学习总结(187)—— 轻量级开源日志框架 tinylog 简介
  8. php 背单词系统_《PHP 编程词典(珍藏版)》
  9. au如何关闭预览编辑器_在线IDE开发入门之从零实现一个在线代码编辑器
  10. DataTable两列转换四列