简单探索ContentProviderOperation
前面一片文章中用到了ContentProviderOperation,那么我们就来看看ContentProviderOperation到底是怎么工作的。
1. ContentProviderOperation实现了Parcelable,构造器是私有的,因此不能直接创建该对象,代码如下:
public class ContentProviderOperation implements Parcelable {
/** @hide exposed for unit tests */
public final static int TYPE_INSERT = 1;
/** @hide exposed for unit tests */
public final static int TYPE_UPDATE = 2;
/** @hide exposed for unit tests */
public final static int TYPE_DELETE = 3;
/** @hide exposed for unit tests */
public final static int TYPE_ASSERT = 4; private final int mType;
private final Uri mUri;
private final String mSelection;
private final String[] mSelectionArgs;
private final ContentValues mValues;
private final Integer mExpectedCount;
private final ContentValues mValuesBackReferences;
private final Map<Integer, Integer> mSelectionArgsBackReferences;
private final boolean mYieldAllowed; private final static String TAG = "ContentProviderOperation";
private ContentProviderOperation(Builder builder) {
mType = builder.mType;
mUri = builder.mUri;
mValues = builder.mValues;
mSelection = builder.mSelection;
mSelectionArgs = builder.mSelectionArgs;
mExpectedCount = builder.mExpectedCount;
mSelectionArgsBackReferences = builder.mSelectionArgsBackReferences;
mValuesBackReferences = builder.mValuesBackReferences;
mYieldAllowed = builder.mYieldAllowed;
}
提供了一些方法来进行RUID操作,代码如下:
public static Builder newInsert(Uri uri) {
return new Builder(TYPE_INSERT, uri);
} /**
* Create a {@link Builder} suitable for building an update
* {@link ContentProviderOperation}.
* @param uri The {@link Uri} that is the target of the update.
* @return a {@link Builder}
*/
public static Builder newUpdate(Uri uri) {
return new Builder(TYPE_UPDATE, uri);
} /**
* Create a {@link Builder} suitable for building a delete
* {@link ContentProviderOperation}.
* @param uri The {@link Uri} that is the target of the delete.
* @return a {@link Builder}
*/
public static Builder newDelete(Uri uri) {
return new Builder(TYPE_DELETE, uri);
} /**
* Create a {@link Builder} suitable for building a
* {@link ContentProviderOperation} to assert a set of values as provided
* through {@link Builder#withValues(ContentValues)}.
*/
public static Builder newAssertQuery(Uri uri) {
return new Builder(TYPE_ASSERT, uri);
}
其中比较难理解的是newAssertQuery方法,该方法并不是用来查询数据的,可以理解为断点查询,也就是查询有没有符合条件的数据,如果没有,会抛出一个OperationApplicationException异常。
public void onClick(View v) {
final ArrayList<ContentProviderOperation> operationList =
new ArrayList<ContentProviderOperation>();
ContentProviderOperation.Builder builder = null;
int rawContactIndex = 0; builder = ContentProviderOperation
.newAssertQuery(RawContacts.CONTENT_URI);
builder.withSelection("version=?", new String[]{String.valueOf(3)})
.withExpectedCount(3);
operationList.add(builder.build()); try {
ContentProviderResult[] res = getContentResolver().
applyBatch(ContactsContract.AUTHORITY, operationList);
Log.e(TAG, "res.length = " + res.length);
for (int i = 0; i < res.length; i++) {
Log.e(TAG, i + "res.toString() = " + res[i].toString());
Log.e(TAG, i + "res.uri = " + res[i].uri);
}
} catch (RemoteException e) {
} catch (OperationApplicationException e) {
}
}
以上这个onClick()方法的作用是查询version==3的数据是否有三条uri对应的表里面,如果为真,res != null,反之会抛出OperationApplicationException异常,其他使用情形可以参照:http://blog.sina.com.cn/s/blog_a387d6d2010114mp.html
2. 我们在使用ContentProviderOperation时,通常是采用这样的方式
builder = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI); 然后我们可以给这个builder添加一些条件,最后调用他的build()方法返回一个ContentProviderOperation对象。看它的
build()方法, 代码如下:
/** Create a ContentProviderOperation from this {@link Builder}. */
public ContentProviderOperation build() {
if (mType == TYPE_UPDATE) {
if ((mValues == null || mValues.size() == 0) &&
(mValuesBackReferences == null ||
mValuesBackReferences.size() == 0)) {
throw new IllegalArgumentException("Empty values");
}
}
if (mType == TYPE_ASSERT) {
if ((mValues == null || mValues.size() == 0) &&
(mValuesBackReferences == null ||
mValuesBackReferences.size() == 0) &&
(mExpectedCount == null)) {
throw new IllegalArgumentException("Empty values");
}
}
return new ContentProviderOperation(this);
}
可以看到new ContentProviderOperation()方法里做了赋值的操作。
3. 最后调用 getContentResolver().applyBatch(ContactsContract.AUTHORITY, operationList) 进行批处理操作,可以这样理解,之前构造ContentProviderOperation的过程就像是在组合SQL语句,而applyBatch相当于执行SQL语句。具体流程是: ContentResolver.java(applyBatch())--> ContentProviderClient.java(applyBatch()) --> ContentProvider.java, ContentProvider的applyBatch()方法如下:
public ContentProviderResult[] applyBatch(
ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
final int numOperations = operations.size();
final ContentProviderResult[] results =
new ContentProviderResult[numOperations];
for (int i = 0; i < numOperations; i++) {
results[i] = operations.get(i).apply(this, results, i);
}
return results;
}
可以看到,用了一个循环,但最终调用的还是ContentProviderOperation的apply方法:
public ContentProviderResult apply(ContentProvider provider,
ContentProviderResult[] backRefs, int numBackRefs) throws OperationApplicationException {
ContentValues values =
resolveValueBackReferences(backRefs, numBackRefs);
首先调用了resolveValueBackReferences()方法, 他会返回mValues或者加工后的mValues:
public ContentValues resolveValueBackReferences(ContentProviderResult[] backRefs, int numBackRefs) {
if () {
return mValues;
}
final ContentValues values;
if (mValues == null) {
values = new ContentValues();
} else {
values = new ContentValues(mValues);
}
for (Map.Entry<String, Object> entry :
mValuesBackReferences.valueSet()) {
String key = entry.getKey();
Integer backRefIndex = mValuesBackReferences.getAsInteger(key);
if (backRefIndex == null) {
Log.e(TAG, this.toString());
throw new IllegalArgumentException("
values backref " + key + " is not an integer");
}
values.put(key, backRefToValue(backRefs, numBackRefs, backRefIndex));
}
return values;
}
为了理解mValuesBackReferences == null这个判断,我们先得知道一个问题,在前面一篇文章中,从第二个ContentProviderOperation开始使用了builder.withValueBackReference(Email.RAW_CONTACT_ID, rawContactIndex),其实就是给mValuesBackReferences赋了值,代码如下:
public Builder withValueBackReference(String key, int previousResult) {
if (mType != TYPE_INSERT && mType != TYPE_UPDATE
&& mType != TYPE_ASSERT) {
throw new IllegalArgumentException("only inserts, updates, and asserts can have value back-references");
}
if (mValuesBackReferences == null) {
mValuesBackReferences = new ContentValues();
}
mValuesBackReferences.put(key, previousResult);
return this;
}
可以看到这个方法不能用于TYPE_DELETE。
继续看resolveValueBackReferences方法,如果mValuesBackReferences == null,直接返回mValues,mValues就是通过withValues()方法填进去的值所组装的ContentValue对象,比如要更新或要插入的值。如果mValuesBackReferences != null,那么会将前一个ContentProviderResult的uri里面的id取出来赋给Email.RAW_CONTACT_ID,就是builder.withValueBackReference(Email.RAW_CONTACT_ID, rawContactIndex)的第一个变量,现在withValueBackReference方法的作用我们也该清楚了。具体的可以参照:http://blog.sina.com.cn/s/blog_a387d6d2010114mp.html
继续看apply()方法:
public ContentProviderResult apply(ContentProvider provider, ContentProviderResult[] backRefs,
int numBackRefs) throws OperationApplicationException {
ContentValues values = resolveValueBackReferences(backRefs, numBackRefs);
String[] selectionArgs =
resolveSelectionArgsBackReferences(backRefs, numBackRefs); if (mType == TYPE_INSERT) {
Uri newUri = provider.insert(mUri, values);
if (newUri == null) {
throw new OperationApplicationException("insert failed");
}
return new ContentProviderResult(newUri);
} int numRows;
if (mType == TYPE_DELETE) {
numRows = provider.delete(mUri, mSelection, selectionArgs);
} else if (mType == TYPE_UPDATE) {
numRows = provider.update(mUri, values, mSelection, selectionArgs);
} else if (mType == TYPE_ASSERT) {
// Assert that all rows match expected values
String[] projection = null;
if (values != null) {
// Build projection map from expected values
final ArrayList<String> projectionList = new ArrayList<String>();
for (Map.Entry<String, Object> entry : values.valueSet()) {
projectionList.add(entry.getKey());
}
projection = projectionList.toArray(new String[projectionList.size()]);
}
final Cursor cursor = provider.query(mUri, projection, mSelection, selectionArgs, null);
try {
numRows = cursor.getCount();
if (projection != null) {
while (cursor.moveToNext()) {
for (int i = 0; i < projection.length; i++) {
final String cursorValue = cursor.getString(i);
final String expectedValue = values.getAsString(projection[i]);
if (!TextUtils.equals(cursorValue, expectedValue)) {
// Throw exception when expected values don't match
Log.e(TAG, this.toString());
throw new OperationApplicationException("Found value " + cursorValue
+ " when expected " + expectedValue + " for column "
+ projection[i]);
}
}
}
}
} finally {
cursor.close();
}
} else {
Log.e(TAG, this.toString());
throw new IllegalStateException("bad type, " + mType);
} if (mExpectedCount != null && mExpectedCount != numRows) {
Log.e(TAG, this.toString());
throw new OperationApplicationException("wrong number of rows: " + numRows);
} return new ContentProviderResult(numRows);
}
我们可以发现,ContentProviderOperation的apply()方法才是真正的执行RUID操作的地方。
同时我们在上面的代码中并未发现使用事务,如果我们要求操作失败时需回滚,那么就应该添加事务,经过上面的分析,可以发现一个比较好的思路就是在我们自己的ContentProvider里面重写appltBatch()方法,并在其中添加事务,后面会有专门分析ContactsProvider文章,我们会看到,其实联系人的ContactsProcider中采用的就是这个思路。
简单探索ContentProviderOperation的更多相关文章
- Servlet过滤器简单探索
过滤器的工作时机介于浏览器和Servlet请求处理之间,可以拦截浏览器对Servlet的请求,也可以改变Servlet对浏览器的响应. 其工作模式大概是这样的: 一.Filter的原理 在Servle ...
- 飘逸的python - 简单探索time模块
time模块中方法众多,不过在了解本质和联系之后,就会发现其实很简单. 在python中可以用3种方式来表达时间.看似很乱,其实就只是这3种变来变去来回转换而已. 1.时间戳 2.时间tuple,由9 ...
- 关于<label>的for属性的简单探索
在freecodecamp上HTML教程的Create a Set of Radio Buttons这一节中,看到这样一段话, It is considered best practice to se ...
- 【SQL SERVER重新认识】数据内部存储结构简单探索
数据库经常需要打交道,但是从来没想过数据库内部是如何存储数据. 今天探索一下数据库内部如何存储数据,从下面几个方面探索 数据库内部如何存储数据 索引数据如何存储 操作数据对存储影响 总结 数据库内部如 ...
- C++的STL中vector内存分配方法的简单探索
STL中vector什么时候会自动分配内存,又是怎么分配的呢? 环境:Linux CentOS 5.2 1.代码 #include <vector> #include <stdio ...
- 【Android开发学习笔记】【高级】【随笔】插件化——Activity生命周期
前言 如同第一章我们说的,宿主程序通过 dexclassloader 将插件的类加载进来,然后通过反射去调用它的方法,这样Activity就被当成了一个普通的类来执行了,因此系统不再接管它的生命周期, ...
- JS实现动画原理一(闭包方式)
前提: 你必须了解js的闭包(否则你看不懂滴) 我们就来做一个js实现jq animate的动画效果来简单探索一下,js动画实现的简单原理: html代码 <div id=&q ...
- CorAnimation7-高效绘图、图像IO以及图层性能
高效绘图 软件绘图 术语绘图通常在Core Animation的上下文中指代软件绘图(意即:不由GPU协助的绘图).在iOS中,软件绘图通常是由Core Graphics框架完成来完成.但是,在一些必 ...
- 【转】 iOS-Core-Animation-Advanced-Techniques(七)
高效绘图.图像IO以及图层性能 高效绘图 原文:http://www.cocoachina.com/ios/20150106/10840.html 不必要的效率考虑往往是性能问题的万恶之源. ——Wi ...
随机推荐
- 阻塞与非阻塞的IO网络读写
看我之前的文章就知道,一般对于网络读的socket,都会加上O_NONBLOCK,非阻塞的选项. int setnonblocking(int fd) { int old_option = fcntl ...
- 20160808_Linux服务
1. http://bbs.csdn.net/topics/370100269 2. http://blog.csdn.net/csfreebird/article/details/8239933 h ...
- 【BZOJ 2818】Gcd - 筛法求素数&phi()
题目描述 给定整数,求且为素数的数对有多少对. 分析 首先筛出所有的素数. 我们考虑枚举素数p,统计满足的个数,等价于统计的个数,即统计以内满足互质的有序数对个数. 不难发现,也就是说,我们只要预处理 ...
- 根据List中对象的某一属性进行排序
不多说,直接看代码: package test; import java.util.ArrayList; import java.util.Collections; import java.util. ...
- C#高级知识点概要(2) - 线程和并发
原文地址:http://www.cnblogs.com/Leo_wl/p/4192935.html 我也想过跳过C#高级知识点概要直接讲MVC,但经过前思后想,还是觉得有必要讲的.我希望通过自己的经验 ...
- 20145218 《Java程序设计》第四周学习总结
20145218 <Java程序设计>第四周学习总结 教材学习内容总结 继承 继承共同行为 继承基本上就是避免多个类间重复定义共同行为. 继承的三个好处:减少代码冗余:维护变得简单:扩展变 ...
- python 练习 7
#!/usr/bin/python # -*- coding: utf-8 -*- def gcd(x,y): #最大公因子 if x>y:x%=y while x: x,y=y%x,x ret ...
- WLAN历史概述-01
无线网络介绍 无线网络的初步应用,可以追朔到第二次世界大战期间,当时美国陆军采用无线电信号做资料的传输.他们研发出了一套无线电传输科技,并且采用相当高强度的加密技术,得到美军和盟军的广泛使用.他们也许 ...
- (01)odoo8.0_Ubuntu14.04源码安装
作者:陈伟明联系 : QQ 942923305 | 微信 toby942923305E-mail: toby2chen@hotmail.com============================ ...
- struts2 mybatis spring hibernate 框架 pom.xml配置 下载地址
访问以下地址:搜索需要框架的配置 选择需要的版本 http://mvnrepository.com