通讯录2014-4-01

来源:互联网 发布:淘宝权重是什么意思 编辑:程序博客网 时间:2024/06/14 23:57

关于手机通讯录的那点事

原文http://rdc.taobao.org/?p=181

在很多应用中,我们都需要获取用户的通讯录信息去进行例如短信,邮件分享的功能。而在现在的Android和iOS等智能机中,系统除了提供数据访问的接口,也将这一类的信息的访问包装为一种服务,提供给各类应用使用。那么,本篇文章就来八一八如何快速访问通讯录获取信息,如何获取通讯录的元数据,以及其中该注意的权限和调试问题。

1. 基本访问篇

无论是在iOS还是Android中,通讯录作为系统应用除了提供本身检索编辑功能外,还讲此类功能包装为服务,供其他应用调用。而通过调用此类系统服务,可以快速和方便的实现访问通讯录的功能,而无需关注数据获取和展示的细节,大大的节省了开发的时间和成本。下面,我们就分别介绍一下iOS和Anroid这两个系统中,如何调用系统通讯录的服务。

iOS

·Framework·

使用iOS通讯录需要先引入两个 framework

  • AddressBook.framework
    • 提供接口让用户访问通讯录内容
  • AddressBookUI.framework对 AddressBook.framework 的数据进一步封装,提供通讯录界面让 app 接入,app 可以通过这个 framework 完成通讯录的一系列操作。
    1. ABPeoplePickerNavigationController:提供 navigation controller 并可以选择用户,选择后可以利用 ABPeoplePickerNavigationControllerDelegate 接收和处理回调信息。
    2. ABPersonViewController:显示通讯录中一个人的详细信息
    3. ABNewPersonViewController:创建一个新的联系人
    4. ABUnknownPersonViewController: 编辑一个联系的信息

·AddressBookUI使用

在非通讯应用中,大多情况都可以直接用AddressBookUI来满足。下面是是一个使用场景,从通讯录中选择一个人,选择他的一个电话号码并发送邀请短信。

需要做的事情:

  1. 为当前target添加 AddressBook 和 AddressBookUI 两个framework,并在需要使用的地方import
  2. 实现 ABPeoplePickerNavigationControllerDelegate 接口,处理 ABPeoplePickerNavigationController 的事件回调
  3. 使用 presentModalViewController:animated: 方法弹出 ABPeoplePickerNavigationController

弹出通讯录代码:

ABPeoplePickerNavigationControllerDelegate 回调:

  1. 取消事件
  2. 选择了通讯录中的一个人,返回 YES 会显示这个人的详情,显示内容是 picker.displayedProperties 里面定义的;返回 NO 就不做任何事情。这里我会弹出一个 UIActionSheet 提供给用户选择电话号码,当用户选择一个电话号码后,就可以弹出发送短信的界面(由于不是这里的重点,这部分代码略)。
  3. 进入一个人的详情页面后,选择一个人的某个属性所发生的事件, 返回 YES 就会发生默认时间,比如打电话,发邮件等,返回 NO 就不做任何事情。由于这个例子中不会进入用户详情页面,所以这个回调没有做任何处理。

      至此,从通讯录中选择一个人,选择他的一个电话号码并发送邀请短信这个时间所需要流程结束。

      Android

      在Android系统中,通过Intent可以很方便的调用系统通讯录的相关界面。打开系统通讯录源码中的AndroidManifest.xml可以看到

      因此,构造以下的Intent,就会打开响应android.intent.action.PICK的Activity,即系统通讯录的联系人列表页面

      此处Intent的data type与AndroidManifest.xml中的data字段相对应,不同的type会使得系统通讯录用不同的数据来填充列表,可以将这个type看作是联系人数据的filter。
      在onActivityResult(int requestCode, int resultCode, Intent data) 中,系统会将用户选择的某个联系人以uri的方式返回给调用者。

      值得注意的几点:

      1. 如果系统中存在多个应用程序能够响应android.intent.action.PICK,并且符合intent中设置的data type的话,调用该intent系统会首先提供一个所有该类应用程序的列表给用户选择。而不同的应用程序在返回值的内容和格式上并不一定完全遵守规范,从而导致调用者得到错误的返回信息。
      2. 通过在intent中指定包名的方式,可以避免上述问题。例如:
      3. 但是通过这样的方式,同样会带来一个适配的问题。在目前诸多rom中,例如HTC,moto等,会改变系统应用的包名,加上厂商的前缀例如com.moto.android.*。上述调用方式,会在运行时因找不到包名而crash退出。因此,我们可以先使用
        PackageManager的getInstalledApplications(int flag)
        方法获取系统安装的应用,来检查是否含有要调用的包名。如果不含有该包名,则通过只指定action的方式来调用intent。

      2. 进阶访问篇

      上述的基础访问篇,通过系统提供的picker可以很方便的对通讯录数据进行获取和查询,节省了开发的时间和成本。但是通常在业务中,我们会遇到更为复杂的需求,例如对通讯录进行定制的排序或者更为复杂的选取交互,这个时候我们需要使用更为灵活的方式对通讯录进行访问。通过获取通讯录的原数据,对数据进行封装,可以满足更为复杂的业务需求。

      iOS:

      主要使用的两个类:

      • ABAddressBookRef
        • 获取通讯录对象,可以遍历它来获取通讯录中所有的人
      • ABRecordRef
        • 通讯录中一个人的对象,可以通过这个对象来获取所有用户的个人信息。

      在使用这两个类的时候,需要特别注意释放内存的方式,因为这个framework都是用C语言写的,所以不会支持arc模式。释放内存的时候需要使用 CFRelease() 方法。因此,如果使用这种访问方式的话,最好还是使用一下适配器模式,统一内存的管理。

      抽取通讯录列表的代码:

      Android:

      Android系统使用sqlite作为数据库,再sqlite上封装了一层ContentProvider,由provider来提供访问的API。通过以下语句,我们可以获取一个手机内的通讯录信息

      • Contacts.CONTENT_URI代表数据库中的Contacts table
      • projection指定了我们需要访问的Columns, 一般我们访问通讯录需要指定Contacts.ID, Contacts.DISPLAY_NAME_PRIMARY, Contacts.PHOTO_ID, Phone.NUMBER等数据
      • selection和selectionArgs类似于query语句中where的作用

      值得一提的是ContentProvider通过uri映射的方式来找到相应的table,而uri不仅可以用作映射到table,也可以映射到某一个column上,例如系统提供了Contacts.CONTACTS_LOOKUP_ID的方式,来直接访问某个contacts ID的联系人信息。此处的机制可以参考ContactsProvider.java和ContactsDatabaseHelper.java两个源文件,查看系统是如何将这些参数组织成sqlite的语句。

      该API返回一个Cursor的对象,封装了projection中指定的数据,通过类似以下代码获得联系人姓名,号码。

      3. 权限篇

      通讯录的数据属于用户的个人隐私,因此无论是iOS还是Android都会对应用访问通讯录进行权限检查。

      iOS:

      iOS6之前系统并不会检查应用访问的权限,因此应用可以通过上述的两种方法静默的访问通讯录数据。在iOS6之后,系统增加了隐私权限的控制,设置菜单中也增加了隐私一项,让用户可以随时对应用访问隐私数据进行关闭和开启。

      因此,在iOS5上开发的访问模块在移植到iOS6上可能会直接失效,在没有申请权限之前,系统会直接返回一个空的ABAddressBookRef,而不作任何提示。下面就详细说一下,如何将通讯录访问移植到iOS6上

      示例代码如下:

      权限申请对于每个应用来说,只会在第一次调用的时候提示用户,用户无论是同意还是拒绝都会被系统记住,下次更改只能由用户手动在系统设置菜单中更改。

      Android:

      Android的权限申请在AndroidManifest.xml中加入权限申明:

      如果不申明该权限的话,在运行时访问Contacts URI的任何函数都会导致程序crash退出。

      4. 调试篇

      iOS:

      在权限访问的调试上,iOS可以说是非常坑爹,系统会将应用申请权限的情况记录以bundle ID作为key存储下来,即便是删除应用也没有用(类似push注册),编者这里提供一个不算很方便的方法,通过更改plist中的bundle ID,让系统识别为一个新的应用,并且使用wildcard provisioning来做应用签名,即带*的provisioning,可以绕开这个坑。不过要记得在调试完之后改回来,否则无法用于发布。

      Android:

      Android的权限同样是静态注册在系统信息中,但是在应用被删除的时候相关信息会被清除,因此不存在调试上的问题。

    0 0
    原创粉丝点击