简单探索ContentProviderOperation_移动开发_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > 移动开发 > 简单探索ContentProviderOperation

简单探索ContentProviderOperation

 2013/12/16 21:09:03  wlrhnh  博客园  我要评论(0)
  • 摘要:前面一片文章中用到了ContentProviderOperation,那么我们就来看看ContentProviderOperation到底是怎么工作的。1.ContentProviderOperation实现了Parcelable,构造器是私有的,因此不能直接创建该对象,代码如下:1publicclassContentProviderOperationimplementsParcelable
  • 标签:operation ide Ten Opera

前面一片文章中用到了ContentProviderOperation,那么我们就来看看ContentProviderOperation到底是怎么工作的。

1. ContentProviderOperation实现了Parcelable,构造器是私有的,因此不能直接创建该对象,代码如下:

 1 public class ContentProviderOperation implements Parcelable {
 2     /** @hide exposed for unit tests */
 3     public final static int TYPE_INSERT = 1;
 4     /** @hide exposed for unit tests */
 5     public final static int TYPE_UPDATE = 2;
 6     /** @hide exposed for unit tests */
 7     public final static int TYPE_DELETE = 3;
 8     /** @hide exposed for unit tests */
 9     public final static int TYPE_ASSERT = 4;
10  
11     private final int mType;
12     private final Uri mUri;
13     private final String mSelection;
14     private final String[] mSelectionArgs;
15     private final ContentValues mValues;
16     private final Integer mExpectedCount;
17     private final ContentValues mValuesBackReferences;
18     private final Map<Integer, Integer> mSelectionArgsBackReferences;
19     private final boolean mYieldAllowed;
20  
21     private final static String TAG = "ContentProviderOperation";
22     private ContentProviderOperation(Builder builder) {
23         mType = builder.mType;
24         mUri = builder.mUri;
25         mValues = builder.mValues;
26         mSelection = builder.mSelection;
27         mSelectionArgs = builder.mSelectionArgs;
28         mExpectedCount = builder.mExpectedCount;
29         mSelectionArgsBackReferences = builder.mSelectionArgsBackReferences;
30         mValuesBackReferences = builder.mValuesBackReferences;
31         mYieldAllowed = builder.mYieldAllowed;
32     }

提供了一些方法来进行RUID操作,代码如下:

 1     public static Builder newInsert(Uri uri) {
 2         return new Builder(TYPE_INSERT, uri);
 3     }
 4  
 5     /**
 6      * Create a {@link Builder} suitable for building an update
 7      * {@link ContentProviderOperation}.
 8      * @param uri The {@link Uri} that is the target of the update.
 9      * @return a {@link Builder}
10      */
11     public static Builder newUpdate(Uri uri) {
12         return new Builder(TYPE_UPDATE, uri);
13     }
14  
15     /**
16      * Create a {@link Builder} suitable for building a delete
17      * {@link ContentProviderOperation}.
18      * @param uri The {@link Uri} that is the target of the delete.
19      * @return a {@link Builder}
20      */
21     public static Builder newDelete(Uri uri) {
22         return new Builder(TYPE_DELETE, uri);
23     }
24  
25     /**
26      * Create a {@link Builder} suitable for building a
27      * {@link ContentProviderOperation} to assert a set of values as provided
28      * through {@link Builder#withValues(ContentValues)}.
29      */
30     public static Builder newAssertQuery(Uri uri) {
31         return new Builder(TYPE_ASSERT, uri);
32     }

其中比较难理解的是newAssertQuery方法,该方法并不是用来查询数据的,可以理解为断点查询,也就是查询有没有符合条件的数据,如果没有,会抛出一个OperationApplicationException异常

 1 public void onClick(View v) {
 2     final ArrayList<ContentProviderOperation> operationList =
 3              new ArrayList<ContentProviderOperation>();
 4     ContentProviderOperation.Builder builder = null;
 5     int rawContactIndex = 0;
 6  
 7     builder = ContentProviderOperation
 8             .newAssertQuery(RawContacts.CONTENT_URI);
 9     builder.withSelection("version=?", new String[]{String.valueOf(3)})
10             .withExpectedCount(3);
11     operationList.add(builder.build());
12  
13     try {
14         ContentProviderResult[] res = getContentResolver().
15             applyBatch(ContactsContract.AUTHORITY, operationList);
16         Log.e(TAG, "res.length = " + res.length);
17         for (int i = 0; i < res.length; i++) {
18             Log.e(TAG, i + "res.toString() = " + res[i].toString());
19             Log.e(TAG, i + "res.uri = " + res[i].uri);
20         }
21     } catch (RemoteException e) {
22     } catch (OperationApplicationException e) {
23     }
24 }

以上这个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()方法, 代码如下:
 1         /** Create a ContentProviderOperation from this {@link Builder}. */
 2         public ContentProviderOperation build() {
 3             if (mType == TYPE_UPDATE) {
 4                 if ((mValues == null || mValues.size() == 0) &&
 5                     (mValuesBackReferences == null ||
 6                      mValuesBackReferences.size() == 0)) {
 7                         throw new IllegalArgumentException("Empty values");
 8                 }
 9             }
10             if (mType == TYPE_ASSERT) {
11                 if ((mValues == null || mValues.size() == 0) &&
12                     (mValuesBackReferences == null ||
13                      mValuesBackReferences.size() == 0) &&
14                      (mExpectedCount == null)) {
15                        throw new IllegalArgumentException("Empty values");
16                 }
17             }
18             return new ContentProviderOperation(this);
19         }

可以看到new ContentProviderOperation()方法里做了赋值的操作。

3. 最后调用 getContentResolver().applyBatch(ContactsContract.AUTHORITY, operationList) 进行批处理操作,可以这样理解,之前构造ContentProviderOperation的过程就像是在组合SQL语句,而applyBatch相当于执行SQL语句。具体流程是: ContentResolver.java(applyBatch())--> ContentProviderClient.java(applyBatch()) --> ContentProvider.java, ContentProvider的applyBatch()方法如下:

 1 public ContentProviderResult[] applyBatch(
 2         ArrayList<ContentProviderOperation> operations)
 3         throws OperationApplicationException {
 4     final int numOperations = operations.size();
 5     final ContentProviderResult[] results =
 6           new ContentProviderResult[numOperations];
 7     for (int i = 0; i < numOperations; i++) {
 8         results[i] = operations.get(i).apply(this, results, i);
 9     }
10     return results;
11 }

可以看到,用了一个循环,但最终调用的还是ContentProviderOperation的apply方法:

1 public ContentProviderResult apply(ContentProvider provider,
2           ContentProviderResult[] backRefs, int numBackRefs) throws OperationApplicationException {
3     ContentValues values =
4           resolveValueBackReferences(backRefs, numBackRefs);

首先调用了resolveValueBackReferences()方法, 他会返回mValues或者加工后的mValues:

 1 public ContentValues resolveValueBackReferences(ContentProviderResult[] backRefs, int numBackRefs) {
 2     if () {
 3         return mValues;
 4     }
 5     final ContentValues values;
 6     if (mValues == null) {
 7         values = new ContentValues();
 8     } else {
 9         values = new ContentValues(mValues);
10     }
11     for (Map.Entry<String, Object> entry :
12         mValuesBackReferences.valueSet()) {
13         String key = entry.getKey();
14         Integer backRefIndex = mValuesBackReferences.getAsInteger(key);
15         if (backRefIndex == null) {
16             Log.e(TAG, this.toString());
17             throw new IllegalArgumentException("
18                    values backref " + key + " is not an integer");
19         }
20         values.put(key, backRefToValue(backRefs, numBackRefs, backRefIndex));
21     }
22     return values;
23 }

为了理解mValuesBackReferences == null这个判断,我们先得知道一个问题,在前面一篇文章中,从第二个ContentProviderOperation开始使用了builder.withValueBackReference(Email.RAW_CONTACT_ID, rawContactIndex),其实就是给mValuesBackReferences赋了值,代码如下:

 1 public Builder withValueBackReference(String key, int previousResult) {
 2     if (mType != TYPE_INSERT && mType != TYPE_UPDATE
 3                              && mType != TYPE_ASSERT) {
 4         throw new IllegalArgumentException("only inserts, updates, and asserts can have value back-references");
 5     }
 6     if (mValuesBackReferences == null) {
 7         mValuesBackReferences = new ContentValues();
 8     }
 9     mValuesBackReferences.put(key, previousResult);
10     return this;
11 }

可以看到这个方法不能用于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()方法:

 1 public ContentProviderResult apply(ContentProvider provider, ContentProviderResult[] backRefs,
 2             int numBackRefs) throws OperationApplicationException {
 3         ContentValues values = resolveValueBackReferences(backRefs, numBackRefs);
 4         String[] selectionArgs =
 5                 resolveSelectionArgsBackReferences(backRefs, numBackRefs);
 6 
 7         if (mType == TYPE_INSERT) {
 8             Uri newUri = provider.insert(mUri, values);
 9             if (newUri == null) {
10                 throw new OperationApplicationException("insert failed");
11             }
12             return new ContentProviderResult(newUri);
13         }
14 
15         int numRows;
16         if (mType == TYPE_DELETE) {
17             numRows = provider.delete(mUri, mSelection, selectionArgs);
18         } else if (mType == TYPE_UPDATE) {
19             numRows = provider.update(mUri, values, mSelection, selectionArgs);
20         } else if (mType == TYPE_ASSERT) {
21             // Assert that all rows match expected values
22             String[] projection =  null;
23             if (values != null) {
24                 // Build projection map from expected values
25                 final ArrayList<String> projectionList = new ArrayList<String>();
26                 for (Map.Entry<String, Object> entry : values.valueSet()) {
27                     projectionList.add(entry.getKey());
28                 }
29                 projection = projectionList.toArray(new String[projectionList.size()]);
30             }
31             final Cursor cursor = provider.query(mUri, projection, mSelection, selectionArgs, null);
32             try {
33                 numRows = cursor.getCount();
34                 if (projection != null) {
35                     while (cursor.moveToNext()) {
36                         for (int i = 0; i < projection.length; i++) {
37                             final String cursorValue = cursor.getString(i);
38                             final String expectedValue = values.getAsString(projection[i]);
39                             if (!TextUtils.equals(cursorValue, expectedValue)) {
40                                 // Throw exception when expected values don't match
41                                 Log.e(TAG, this.toString());
42                                 throw new OperationApplicationException("Found value " + cursorValue
43                                         + " when expected " + expectedValue + " for column "
44                                         + projection[i]);
45                             }
46                         }
47                     }
48                 }
49             } finally {
50                 cursor.close();
51             }
52         } else {
53             Log.e(TAG, this.toString());
54             throw new IllegalStateException("bad type, " + mType);
55         }
56 
57         if (mExpectedCount != null && mExpectedCount != numRows) {
58             Log.e(TAG, this.toString());
59             throw new OperationApplicationException("wrong number of rows: " + numRows);
60         }
61 
62         return new ContentProviderResult(numRows);
63     }

我们可以发现,ContentProviderOperation的apply()方法才是真正的执行RUID操作的地方。

同时我们在上面的代码中并未发现使用事务,如果我们要求操作失败时需回滚,那么就应该添加事务,经过上面的分析,可以发现一个比较好的思路就是在我们自己的ContentProvider里面重写appltBatch()方法,并在其中添加事务,后面会有专门分析ContactsProvider文章,我们会看到,其实联系人的ContactsProcider中采用的就是这个思路。

发表评论
用户名: 匿名