简单探索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 ...
随机推荐
- C#_abstract的用法
/// <summary> /// 抽像类 /// </summary> public abstract class Hello { private string msg = ...
- Linux命令(1) - 查看内存使用情况: free -hm
使用"free -hm"命令查看linux服务器的内存使用状况,其中-h表示人性化显示,-m表示将内存显示为M:
- 为什么删不掉date模块
显示是field pending deletion一看report里面的field list并没有xxx_date_xxx,只好跑到数据库才看到一个field_date_test当时并没有把这个字段当 ...
- HTTP 头部解释
1. Accept:告诉WEB服务器自己接受什么介质类型,*/* 表示任何类型,type/* 表示该类型下的所有子类型,type/sub-type. 2. Accept-Charset:浏览器申明自己 ...
- 基因组组装工具之 SOAPdenovo 使用方法
SOAPdenovo是一个新颖的适用于组装短reads的方法,能组装出类似人类基因组大小的de novo草图. 该软件特地设计用来组装Illumina GA short reads,新的版本减少了在图 ...
- ToolBar Style
colorPrimaryDark 状态栏背景色. 在 style 的属性中设置. textColorPrimary App bar 上的标题与更多菜单中的文字颜色. 在 style 的属性中设置. A ...
- js中正则表达式 书写方法
function test(){ var text="index.aspx?test=1&ww=2&www=3"; var re = ...
- WebContentGenerator
用于提供如浏览器缓存控制.是否必须有session开启.支持的请求方法类型(GET.POST等)等,该类主要有如下属性: Set<String> supportedMethods:设置 ...
- media query ie8- 兼容实现总结
虽然说响应式设计的理想状态是,需对pc/移动各种终端进行响应:但是现实是高分辨率的pc端与手机终端屏幕相差太大,像电商这样有大量图片和文字信息的同时排版要求精准的页面,设计一个同时适应高分辨率pc又适 ...
- javascript之with的使用 弊端
妹的,昨天都快写完了,一不小心点了个关闭,然后...就没有然后了 wordpress的自动保存功能咋就这么不靠谱呢 记得还在懵懂学习JavaScript基础之时,坊间便有传言“with语句是低效率语句 ...