简单探索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 ...
随机推荐
- python 在不同层级目录import 模块的方法
有一个文件夹/home/a, 里面有个模块叫b.py, 我怎么把他import到程序里? 1). import sys; sys.path.append("/home/a/") ...
- swift语言学习之UITableView分割线左边到头的解决
此方法兼容ios 7.8.9 // 在tableView创建地方设置 if tableView!.respondsToSelector("setSeparatorInset:") ...
- Hexo+github 搭建个人博客(一)
一.软件环境准备 1.安装git windows下载exe安装:linux 执行 apt-get install git-core 安装 2.安装Node.js windows使用 msi 文件进行安 ...
- WinForm应用程序之注册模块的设计与实现
Posted on 2012-11-13 10:21 星星之火116 阅读(3260) 评论(4) 编辑 收藏 我们在安装一些桌面应用程序的时候,往往在会有提示当前用户使用的是试用版,要进行注册.刚好 ...
- 4.2 EF的CRUD控制器代码
以下的例子以留言本作为依据. 1.添加 public ActionResult Create() { return View(); } // // POST: /Contact/Create [Htt ...
- 第四周 课堂Scrum站立会议
项目名称:连连看游戏(C#) 小组名称:4Boys 小组成员:武志远.李权.张金生.张政 站立会议内容 昨天完成的: 1.完成了游戏界面 2.做查阅关于技术方面的资料并且发布博客 3.发布项目的进度p ...
- hdu 4081 Qin Shi Huang's National Road System (次小生成树)
Qin Shi Huang's National Road System Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/3 ...
- java基础学习之 消息对话款
package Dome; import java.awt.event.*; import java.awt.*; import javax.swing.*; public class WindowM ...
- asp.net 奇淫技巧
VS2013自动注释插件 分享一个Visual Studio的背景插件,让堆码更富情趣 工欲善其事必先利其器系列之:更换Visual Studio代码风格. 为Visual Studio更换皮肤和背景 ...
- FireFox 一键清理缓存
https://addons.mozilla.org/zh-CN/firefox/addon/empty-cache-button/