簡単探索ContentProviderOperation

36421 ワード

前の記事で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対応のテーブルが3つあるかどうかを調べることです.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.AUTHEORITY,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という判断を理解するために、まず一つの問題を知っておく必要があります.前の文章では、2番目のContentProviderOperationからbuilderを使用しています.withValueBackReference(Email.RAW_CONTART_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_CONTART_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で採用されているのがこの考え方です.