简单探索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 ...
随机推荐
- ora-01033:oracle initialization or shutdown in progress 解决方法
今天研究Oracle遇到了这个问题ora-01033:oracle initialization or shutdown in progress,经过分析研究终于解决了,写下来纪念一下.我的库是ora ...
- Python中的Class的讨论
尽管Python在Function Programming中有着其他语言难以企及的的优势,但是我们也不要忘了Python也是一门OO语言哦.因此我们关注Python在FP上的优势的同时,还得了解一下P ...
- C#_接口
.Net提供了接口,这个不同于Class或者Struct的类型定义.接口有些情况,看似和抽象类一样,因此有些人认为在.Net可以完全用接口来替换抽象类.其实不然,接口和抽象类各有长处和缺陷,因此往往在 ...
- R语言实战
教材目录 第一部分 入门 第一章 R语言介绍 第二章 创建数据集 第三章 图形初阶 第四章 基本数据管理 第五章 高级数据管理 第二部分 基本方法 第六章 基本图形 第七章 基本统计方法 第三部分 中 ...
- 【EasyX】RGB to Gray
code: #include <graphics.h> #include <conio.h> void main() { initgraph(, ); // 读取图片 load ...
- [翻译]了解ASP.NET底层架构(八)
原文地址:http://www.cnblogs.com/tmfc/archive/2006/09/04/493304.html [翻译]了解ASP.NET底层架构(完) [翻译]了解ASP.NET底层 ...
- 安装Ubuntu Linux系统时硬盘分区最合理的方法
无论是安装Windows还是Linux操作系统,硬盘分区都是整个系统安装过程中最为棘手的环节,网上的一些Ubuntu Linux安装教程一般都是自动分区,给初学者带来很大的不便,下面我就根据多年来在合 ...
- [JavaScript] 函数同名问题
存在同名函数时,最后的函数会覆盖掉以前的同名函数. var x = 1, y = z = 0; function add(n) { return n = n + 1; } y = add(x); fu ...
- ubuntu下安装基本配置
安装ubuntu更新: sudo apt-get update sudo apt-get upgrade 安装Docky: sudo add-apt-repository ppa:ricotz/doc ...
- C++ 与设计模式学习(其一)
记得曾经一年前,听到同学再说设计模式,当时觉得不怎么重要,所以就没有去系统的学习,这一放,就是一年,直到前段时间,面试了一个阿里巴巴的职位,要我谈谈对于设计模式的看法. 之后就好好了看了一些文章,好好 ...