通讯录2014-4-01
来源:互联网 发布:淘宝权重是什么意思 编辑:程序博客网 时间:2024/06/14 23:57
关于手机通讯录的那点事
在很多应用中,我们都需要获取用户的通讯录信息去进行例如短信,邮件分享的功能。而在现在的Android和iOS等智能机中,系统除了提供数据访问的接口,也将这一类的信息的访问包装为一种服务,提供给各类应用使用。那么,本篇文章就来八一八如何快速访问通讯录获取信息,如何获取通讯录的元数据,以及其中该注意的权限和调试问题。
1. 基本访问篇
无论是在iOS还是Android中,通讯录作为系统应用除了提供本身检索编辑功能外,还讲此类功能包装为服务,供其他应用调用。而通过调用此类系统服务,可以快速和方便的实现访问通讯录的功能,而无需关注数据获取和展示的细节,大大的节省了开发的时间和成本。下面,我们就分别介绍一下iOS和Anroid这两个系统中,如何调用系统通讯录的服务。
iOS
·Framework·
使用iOS通讯录需要先引入两个 framework
- AddressBook.framework
- 提供接口让用户访问通讯录内容
- AddressBookUI.framework对 AddressBook.framework 的数据进一步封装,提供通讯录界面让 app 接入,app 可以通过这个 framework 完成通讯录的一系列操作。
- ABPeoplePickerNavigationController:提供 navigation controller 并可以选择用户,选择后可以利用 ABPeoplePickerNavigationControllerDelegate 接收和处理回调信息。
- ABPersonViewController:显示通讯录中一个人的详细信息
- ABNewPersonViewController:创建一个新的联系人
- ABUnknownPersonViewController: 编辑一个联系的信息
·AddressBookUI使用
在非通讯应用中,大多情况都可以直接用AddressBookUI来满足。下面是是一个使用场景,从通讯录中选择一个人,选择他的一个电话号码并发送邀请短信。
需要做的事情:
- 为当前target添加 AddressBook 和 AddressBookUI 两个framework,并在需要使用的地方import
- 实现 ABPeoplePickerNavigationControllerDelegate 接口,处理 ABPeoplePickerNavigationController 的事件回调
- 使用 presentModalViewController:animated: 方法弹出 ABPeoplePickerNavigationController
弹出通讯录代码:
ABPeoplePickerNavigationControllerDelegate 回调:
- 取消事件
- 选择了通讯录中的一个人,返回 YES 会显示这个人的详情,显示内容是 picker.displayedProperties 里面定义的;返回 NO 就不做任何事情。这里我会弹出一个 UIActionSheet 提供给用户选择电话号码,当用户选择一个电话号码后,就可以弹出发送短信的界面(由于不是这里的重点,这部分代码略)。
- 进入一个人的详情页面后,选择一个人的某个属性所发生的事件, 返回 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的方式返回给调用者。值得注意的几点:
- 如果系统中存在多个应用程序能够响应android.intent.action.PICK,并且符合intent中设置的data type的话,调用该intent系统会首先提供一个所有该类应用程序的列表给用户选择。而不同的应用程序在返回值的内容和格式上并不一定完全遵守规范,从而导致调用者得到错误的返回信息。
- 通过在intent中指定包名的方式,可以避免上述问题。例如:
- 但是通过这样的方式,同样会带来一个适配的问题。在目前诸多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的权限同样是静态注册在系统信息中,但是在应用被删除的时候相关信息会被清除,因此不存在调试上的问题。
- 通讯录2014-4-01
- 通讯录开发学习4
- shell 通讯录(4)
- 通讯录
- 通讯录
- 通讯录
- 通讯录
- 通讯录
- 通讯录
- 通讯录
- 通讯录
- 通讯录
- 通讯录
- 通讯录
- 通讯录
- 通讯录
- 通讯录
- 通讯录
- 【大家明白才是真的明白】动态规划算法什么时候能用一维数组解问题
- c++中多线程的简单使用
- hibernate.MappingException: entity class not found
- magento提示Exception printing is disabled by default for security reasons.
- base基本知识和this的比较
- 通讯录2014-4-01
- 设计模式之分类
- cocos2dx GL 效果工具代码
- "阳台男孩"的家暴噩梦:8个小时被爸爸打了17次
- java Math 方法
- python调用gdal方法
- 树莓派学习笔记--环境的配置
- VS 多项目调试 解决方案 多项目 Silverlight WPF
- UniGui中设置uniEdit控件的fieldLabel ,emptyText等Extjs属性