API Guides Contacts Provider (三)
Contacts Provider Access
这一章节讲述了如何从Contacts Provider访问数据。主要关注以下几点:
- Entity queries.实体查询
- Batch modification.批量修改
- Retrieval and modification with intents.使用intents检索和修改
- Data integrity.数据完整性
Querying entities
ContactsContract.RawContacts关联到ContactsContract.Contacts的数据,或者所有管理到
ContactsContract.RawContacts的ContactsContract.CommonDataKinds.Email数据。为了便于实现这个功能,Contacts Provider 提供了实体(entity)的数据结构,它像一个连接各个数据表的数据结构。
注意:一个实体通常没有包含父表和子表所有的列。如果访问到实体没有的列,就会得到一个异常。
下面的代码片段展示了如何检索一个contact的所有raw contact。这个代码片段是一个拥有两个activity的大程序的一部分,这两个activity是“主要”和“详细信息”的activity。“主要”的activity展示一列联系人的数据,当用户选择一个联系人时,把它的ID传给“详细信息”的activity。详细信息的activity使用ContactsContract.Contacts.Entity
展示了改contact的所有Data数据。
下面的代码是“详细信息”activity的代码片段:
.../** Appends the entity path to the URI. In the case of the Contacts Provider, the* expected URI is content://com.google.contacts/#/entity (# is the ID value).*/mContactUri = Uri.withAppendedPath(mContactUri,ContactsContract.Contacts.Entity.CONTENT_DIRECTORY);// Initializes the loader identified by LOADER_ID.getLoaderManager().initLoader(LOADER_ID, // The identifier of the loader to initializenull, // Arguments for the loader (in this case, none)this); // The context of the activity// Creates a new cursor adapter to attach to the list viewmCursorAdapter = new SimpleCursorAdapter(this, // the context of the activityR.layout.detail_list_item, // the view item containing the detail widgetsmCursor, // the backing cursormFromColumns, // the columns in the cursor that provide the datamToViews, // the views in the view item that display the data0); // flags// Sets the ListView's backing adapter.mRawContactList.setAdapter(mCursorAdapter);
...
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {/** Sets the columns to retrieve.* RAW_CONTACT_ID is included to identify the raw contact associated with the data row.* DATA1 contains the first column in the data row (usually the most important one).* MIMETYPE indicates the type of data in the data row.*/String[] projection ={ContactsContract.Contacts.Entity.RAW_CONTACT_ID,ContactsContract.Contacts.Entity.DATA1,ContactsContract.Contacts.Entity.MIMETYPE};/** Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw* contact collated together.*/String sortOrder =ContactsContract.Contacts.Entity.RAW_CONTACT_ID +" ASC";/** Returns a new CursorLoader. The arguments are similar to* ContentResolver.query(), except for the Context argument, which supplies the location of* the ContentResolver to use.*/return new CursorLoader(getApplicationContext(), // The activity's contextmContactUri, // The entity content URI for a single contactprojection, // The columns to retrievenull, // Retrieve all the raw contacts and their data rows.null, //sortOrder); // Sort by the raw contact ID.
}
数据查询完成后, LoaderManager
回调onLoadFinished()。查询的结果会由一个Cursor参数传递。你可以从这个Cursor获取数据用来显示或其他操作。
Batch modification
尽可能使用批量修改来插入、更新和删除Contacts Provider中的数据。通过创建存储ContentProviderOperation的 ArrayList,再调用applyBatch()的方法。因为Contacts Provider在一个transaction中执行applyBatch()的所有操作,所以能够保持数据的一致性。批量操作也可以方便插入一个联系人数据及其详细数据。
注意:如果只是要改一行raw contact的数据,首先考虑用发intent给联系人应用的方式,而不是自己去操作数据。使用intent的方式在Retrieval and modification with intents有更详细的说明。
Yield points
批量修改包含大量的操作可能会阻塞进程,导致用户体验不好。为了把修改操作放在尽可能少的操作列表里面,并且要防止阻塞进程,可以为一个或多个操作设置yield points。 yield point 就是isYieldAllowed()为true的ContentProviderOperation对象。当Contacts Provider执行到yield point的时候,它暂停操作,让其他进程先执行,并关闭当前的操作。当provider重新开始工作后,它继续之前的工作,从操作列表中取出下一个操作,开启一个新的transaction。
Yield point导致调用applyBatch()的时候,会有多个transaction。yield point要设置在一系列相关行操作的最后一个操作。例如,在添加联系人的时候,yield point要设置在添加raw contact和它的data 的最后一个操作,或者添加在一系列跟同一个联系人相关的操作中的最后一个操作。
yield point也是一个原子操作。所有在两个yield point直接操作,在一次操作中要么全部成功,要不全部无效。如果没有设置yield point,整个批量处理就是一个最小的原子操作。如果设置了原子操作,你避免降低系统性能作,而在同一时间,确保操作的子集是原子。
Modification back references
添加一个新的raw contact和它的data数据,使用一系列的ContentProviderOperation对象,你必须把data数据的RAW_CONTACT_ID设置成新添加的raw contact的ID。然而,当创建data数据行的时候,raw contact的ID还没生成。因为这一系列的
ContentProviderOperation对象是一个原子操作,还不能得到创建raw contact的结果。为了解决这个问题,ContentProviderOperation.Builder有一个withValueBackReference()的方法。这个方法可以让你使用前面操作的结果来插入数据。
withValueBackReference()有两个参数:
-
key
key-value键值对的键。值应该是所修改的表的列名。
previousResult
(简单来说,就是操作的索引值)
applyBatch()返回值ContentProviderResult数组的索引。开始批量操作后,每个操作的结果都存在一个数组里。previousResult操作结果数组的一个索引,通过key值检索和存储。这样子就可以在插入一个新的raw contact联系人后,得到它的_ID,然后再你插入Data数据的时候,就可以通过“后引用”去使用这个ID。
在调用applyBatch()的时候,整个结果数组就创建好了,结果数组的大小和ContentProviderOperation的数组大小一样。但是,结果数组的初始值都是null,如果在操作结束前,通过“后引用”来使用操作的结果,就会抛出异常。
下面的代码片段显示如何使用batch插入一个raw contact和数据。这些代码包括使用yield points和“后引用”。这个代码片段是createContacEntry()方法的扩展,是Contact Manager 例子源码中的ContactAdder的一部分。第一个源码片在UI中获取联系人的data数据。用户已经选择了用来添加联系人的账户:// Creates a contact entry from the current UI values, using the currently-selected account. protected void createContactEntry() {/** Gets values from the UI*/String name = mContactNameEditText.getText().toString();String phone = mContactPhoneEditText.getText().toString();String email = mContactEmailEditText.getText().toString();int phoneType = mContactPhoneTypes.get(mContactPhoneTypeSpinner.getSelectedItemPosition());int emailType = mContactEmailTypes.get(mContactEmailTypeSpinner.getSelectedItemPosition());
下一个源码片段创建一个operation去插入一个raw contact数据到ContactsContract.RawContacts:
/** Prepares the batch operation for inserting a new raw contact and its data. Even if* the Contacts Provider does not have any data for this person, you can't add a Contact,* only a raw contact. The Contacts Provider will then add a Contact automatically.*/// Creates a new array of ContentProviderOperation objects.ArrayList<ContentProviderOperation> ops =new ArrayList<ContentProviderOperation>();/** Creates a new raw contact with its account type (server type) and account name* (user's account). Remember that the display name is not stored in this row, but in a* StructuredName data row. No other data is required.*/ContentProviderOperation.Builder op =ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI).withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType()).withValue(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());// Builds the operation and adds it to the array of operationsops.add(op.build());
接下来的代码,创建了显示名字,电话号码和邮箱的data数据。每一个操作使用
withValueBackReference()
去获取RAW_CONTACT_ID。这个引用指向 第一个操作的ContentProviderResult,第一个操作是创建raw contact并返回它的_ID。这样,每个新建的data数据,就自动连接它的
RAW_CONTACT_ID到新插入的ContactsContract.RawContacts
。
ContentProviderOperation.Builder在添加email的时候,使用
withYieldAllowed()添加了一个yield point:
// Creates the display name for the new raw contact, as a StructuredName data row.op =ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)/** withValueBackReference sets the value of the first argument to the value of* the ContentProviderResult indexed by the second argument. In this particular* call, the raw contact ID column of the StructuredName data row is set to the* value of the result returned by the first operation, which is the one that* actually adds the raw contact row.*/.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)// Sets the data row's MIME type to StructuredName.withValue(ContactsContract.Data.MIMETYPE,ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)// Sets the data row's display name to the name in the UI..withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name);// Builds the operation and adds it to the array of operationsops.add(op.build());// Inserts the specified phone number and type as a Phone data rowop =ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)/** Sets the value of the raw contact id column to the new raw contact ID returned* by the first operation in the batch.*/.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)// Sets the data row's MIME type to Phone.withValue(ContactsContract.Data.MIMETYPE,ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)// Sets the phone number and type.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone).withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType);// Builds the operation and adds it to the array of operationsops.add(op.build());// Inserts the specified email and type as a Phone data rowop =ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)/** Sets the value of the raw contact id column to the new raw contact ID returned* by the first operation in the batch.*/.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)// Sets the data row's MIME type to Email.withValue(ContactsContract.Data.MIMETYPE,ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)// Sets the email address and type.withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email).withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType);/** Demonstrates a yield point. At the end of this insert, the batch operation's thread* will yield priority to other threads. Use after every set of operations that affect a* single contact, to avoid degrading performance.*/op.withYieldAllowed(true);// Builds the operation and adds it to the array of operationsops.add(op.build());
最后一个代码片段调用
applyBatch()去插入一个新的raw contact 和它的data数据行。
// Ask the Contacts Provider to create a new contactLog.d(TAG,"Selected account: " + mSelectedAccount.getName() + " (" +mSelectedAccount.getType() + ")");Log.d(TAG,"Creating contact: " + name);/** Applies the array of ContentProviderOperation objects in batch. The results are* discarded.*/try {getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);} catch (Exception e) {// Display a warningContext ctx = getApplicationContext();CharSequence txt = getString(R.string.contactCreationFailure);int duration = Toast.LENGTH_SHORT;Toast toast = Toast.makeText(ctx, txt, duration);toast.show();// Log exceptionLog.e(TAG, "Exception encountered while inserting contact: " + e);}
}
批量操作允许实现一个优化多线程控制( optimistic concurrency control),一个无需锁定底层数据库的方法。使用这个方法,在调用transaction 的时候,检查一下是否有其他的修改会同时发生。如果发现同时修改,就取消这次修改。
Optimistic concurrency control 对手机设备很有用,手机设备只有一个用户的,并且很少同时访问数据库。因为数据库没有加锁,就不用花费时间去加锁和解锁。
以下是使用ContactsContract.RawContacts
的步骤:
- 在检索数据的时候,同时检索raw contact的VERSION列。
- 创建一个ContentProviderOperation.Builder,调用newAssertQuery(Uri)方法,content URI是
RawContacts.CONTENT_URI
和raw contact的_ID. - ContentProviderOperation.Builder调用
withValue()的方法去对比刚刚检索到的
VERSION值 - 在同一个
ContentProviderOperation.Builder中调用
withExpectedCount()
去确保在这次断言中只有一个修改。 - 调用Call
build()
创建ContentProviderOperation
,然后把这个ContentProviderOperation加入到调用applyBatch()操作的第一个操作。
- 调用批量操作。
如果raw contact在你读取数据,然后去修改它的期间被更新, ContentProviderOperation 将会失败,然后整个力量操作都会失效。这是需要重新尝试这次操作。
下面的代码片段展示如何在使用CursorLoader查询一个raw contact后,创建一个断言
ContentProviderOperation
/** The application uses CursorLoader to query the raw contacts table. The system calls this method* when the load is finished.*/
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {// Gets the raw contact's _ID and VERSION valuesmRawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION));
}...// Sets up a Uri for the assert operation
Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, mRawContactID);// Creates a builder for the assert operation
ContentProviderOperation.Builder assertOp = ContentProviderOperation.netAssertQuery(rawContactUri);// Adds the assertions to the assert operation: checks the version and count of rows tested
assertOp.withValue(SyncColumns.VERSION, mVersion);
assertOp.withExpectedCount(1);// Creates an ArrayList to hold the ContentProviderOperation objects
ArrayList ops = new ArrayList<ContentProviderOperationg>;ops.add(assertOp.build());// You would add the rest of your batch operations to "ops" here...// Applies the batch. If the assert fails, an Exception is thrown
try{ContentProviderResult[] results =getContentResolver().applyBatch(AUTHORITY, ops);} catch (OperationApplicationException e) {// Actions you want to take if the assert operation fails go here}
API Guides Contacts Provider (三)相关推荐
- android api 完整通译之Contacts Provider (学习安卓必知的api,中英文对照)
android api 完整翻译之Contacts Provider (学习安卓必知的api,中英文对照) Contacts Provider 电话簿(注:联系人,联络人.通信录)提供者 ------ ...
- Android Content Providers(三)——Contacts Provider
接着上篇Android Content Providers(二)--Contacts Provider继续,接下来要说明的是顶层的Contacts,Contacts是聚合联系人表,在之前讨论的RawC ...
- Android API 指南 - Android API Guides
http://developer.android.com/intl/zh-CN/guide/components/index.html ) 请找到自己要翻译的部分,按下面的'链接规范'添加到下面列表中 ...
- Altium Designer 20 Grids、 Guides、 Axes 三种栅格的讲解
Altium Designer 20 Grids. Guides. Axes 三种栅格的讲解 一.Grids栅格 是我们常用的.默认的普通栅格 Grids栅格常用的设置有: 1.快捷键 G 可以设置栅 ...
- 前端js调用后端API获取数据的三种方法(2022.7.25)
前端js调用后台API获取数据的三种方法(2022.7.25) 前言 需求分析 一个Get实例 浏览器请求 SoapUI软件请求 一个Post实例 浏览器请求 SoapUI软件请求 1.Http简介( ...
- Android API Guides 安卓API指导----第一部分:Introduction(介绍)
第一部分: Introduction(介绍) 目录包含:APP Fundamentals (应用程序基础) Device Compatibility(设备兼容性) System Perm ...
- [翻译]API Guides - Bound Services
官方文档原文地址:http://developer.android.com/guide/components/bound-services.html 一个Bound Service是一个客户端-服务器 ...
- WEB API新增整理(三)
一.Gamepad API 简介: 给予开发者一种简单.统一的方式来识别并响应游戏控制器(手柄).其中包含了三个接口.两个事件.一个特殊函数,用来响应控制器的连接与断开.获取其他关于控制器的信息以及识 ...
- 大数据早报:谷歌Cloud Natural Languages API推新技术 全球三分之二的人口通过移动设备上网(9.21)
数据早知道,上36dsj看早报! 来源36大数据,作者:奥兰多 『谷歌』谷歌Cloud Natural Languages API推新技术:助力新闻行业发展 近日,谷歌在其免费的 Cloud Natu ...
- C++ API 设计 08 第三章 模式
第三章 模式 前一章所讨论的品质是用来区分设计良好和糟糕的API.在接下来的几个章节将重点关注构建高品质的API的技术和原则.这个特殊的章节将涵盖一些有用的设计模式和C++ API设计的相关习惯用法. ...
最新文章
- 【WEB API项目实战干货系列】- API登录与身份验证(三)
- centos6.5下载卸载mysql_Linux CentOS 6.5 卸载、tar安装MySQL
- oracle timestamp比较大小_ORACLE包和过程依赖关系测试
- 基于SRS的RTMP分发技术方案
- 0810 - 代码,还是得一行行写
- 【入门1】顺序结构 (今天刷洛谷了嘛)
- switch一定比if else好用吗
- git push本地代码到github出错
- 极客大学架构师训练营 秒杀 搜索引擎 爬虫 Lucene Elastic Search 第18课 听课总结
- ASO优化方法_获取ASO关键词指数接口
- ubuntu复制粘贴
- DarkComet RAT简介
- gsva gsea ssgsea gaochao 使用GSVA方法计算某基因集在各个样本的表现
- 服务器系统盘40g是什么,云服务器 40g系统盘
- 大数据研究的若干科学问题——徐宗本
- 数据链路层的封装-HDLC协议
- SLAM算法大体流程---理论知识(入门级)
- 裴蜀定理(贝祖定理)
- String.Format将人民币符号改成美元符号{0:C}
- C语言中猜数大小的实验报告,猜数字游戏实验报告