原文出处:http://blog.csdn.net/imuhao/article/details/52002995
ContentProviders 是Android 系统核心组件之一,ContentProviders 封装了数据的访问接口,其底层数据一般都是保存在数据库中或者保存在云端。
有时候你需要更新多行数据,可以选择调用多次ContentResolver的对应函数,或者 使用批量操作。当然 后者性能会比较好些。
为了使批量更新、插入、删除数据更加方便,android系统引入了 ContentProviderOperation类。
在官方开发文档中推荐使用ContentProviderOperations,有一下原因:
要创建ContentProviderOperation对象,则需要使用 ContentProviderOperation.Builder类,通过调用下面几个静态函数来获取一个Builder 对象:
函数 | 说明 |
---|---|
newInsert () | 创建一个用于执行插入操作的Builder |
newUpdate () | 创建一个用于执行更新操作的Builder |
newDelete() | 创建一个用于执行删除操作的Builder |
Builder设计模式,链式编程生成ContentProviderOperation对象
ArrayList ops =
new ArrayList();
ops.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
.withValue(RawContacts.ACCOUNT_TYPE, "someAccountType")
.withValue(RawContacts.ACCOUNT_NAME, "someAccountName")
.withYieldAllowed(true)
.build());
当然 你还可以使用熟悉的ContentValues对象,对应的函数为withValues(values)。
withSelection (String selection, String[] selectionArgs)
指定需要操作的数据条件。只有在更新、删除操作中有用。
withValue (String key, Object value)
定义一列的数据值。只在更新、插入数据中有用。
withValues (ContentValues values)
定义多列的数据值。 只在更新、插入数据中有用
withYieldAllowed(boolean)
批量操作一大堆数据可能会长期锁定数据库,从而阻止其他应用访问该数据库并且有可能会引起ANR(应用无响应)对话框出现。
为了避免长期锁定数据库,只要在批量操作中添加“yield points”即可。一个yield points告诉Content Provider,在执行下一个操作之前可以先提交当前的数据,然后通知其他应用,如果有其他应用请求数据的话,就先让其他应用操作,等其他应用操作完成后,再继续打开一个事务来执行下一个操作。如果没有其他程序请求数据,则一个yield points不会自动提交事务,而是继续执行下一个批量操作。通常情况下一个同步Adapter应该在开始操作一行原数据之前添加一个yield points
withValueBackReference(String key, int previousResult)
在Android中 创建一个联系人,需要先创建一个Raw Contact,然后再创建其他附件的数据(电话号码、email、地址等)。而后面的操作需要Raw Contact的ID值作为外键。 由于用到了 ContentProviderOperation,第一步Raw Contact的id还没有生成呢。 这个时候就可以使用withValueBackReference 函数来实现该功能了。在withValueBackReference 函数中第一个参数为 本次操作数据字段的名称 ;第二个参数为 需要引用前面某一次操作的序号
ops = new ArrayList();
ops.add(ContentProviderOperation.
newInsert(RawContacts.CONTENT_URI)
.withValue(RawContacts.ACCOUNT_TYPE, someAccountType)
.withValue(RawContacts.ACCOUNT_NAME, someAccountName)
.build());
ops.add(ContentProviderOperation.
newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(Data.RAW_CONTACT_ID, 0)
.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
.withValue(StructuredName.DISPLAY_NAME, displayName)
.withValue(StructuredName.FAMILY_NAME, lastName)
.withValue(StructuredName.GIVEN_NAME, firstName)
.build());
ops.add(ContentProviderOperation.
newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(Data.RAW_CONTACT_ID, 0)
.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
.withValue(Phone.NUMBER, phoneNumber)
.withValue(Phone.TYPE, Phone.TYPE_HOME)
.withValue(Phone.LABEL, label)
.build());
ops.add(ContentProviderOperation.
newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(Data.RAW_CONTACT_ID, 0)
.withValue(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE)
.withValue(Event.TYPE, Event.TYPE_BIRTHDAY)
.withValue(Event.START_DATE, dateStr)
.build());
try {
getContentResolver().
applyBatch(ContactsContract.AUTHORITY, ops);
} catch (RemoteException e) {
// some error handling
} catch (OperationApplicationException e) {
// some error handling
}
ContentProviderOperation是放到一个ArrayList中的,第一个(序号为0)操作创建了一个Row Contact,后续的几个操作分别需要第一个操作返货的ID值,该值对应的数据列名称为Data.RAW_CONTACT_ID,所以
withValueBackReference(Data.RAW_CONTACT_ID, 0)两个参数分别为Data.RAW_CONTACT_ID 和 0
最后通过ContentResolver 的applyBatch()函数来应用批量操作:
try {
getContentResolver().
applyBatch(ContactsContract.AUTHORITY, ops);
} catch (RemoteException e) {
// do s.th.
} catch (OperationApplicationException e) {
// do s.th.
}
批量操作很简单,提升性能很容易!
public static void batchAddContact(Context context, List list)
throws RemoteException, OperationApplicationException {
ArrayList ops = new ArrayList<>();
int rawContactInsertIndex = 0;
for (FriendBean contact : list) {
rawContactInsertIndex = ops.size(); // 有了它才能给真正的实现批量添加
ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null)
.withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null)
.withYieldAllowed(true).build());
// 添加姓名
ops.add(ContentProviderOperation
.newInsert(android.provider.ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Contacts.Data.RAW_CONTACT_ID,
rawContactInsertIndex)
.withValue(ContactsContract.Contacts.Data.MIMETYPE,
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
contact.getName())
.withYieldAllowed(true).build());
// 添加号码
ops.add(ContentProviderOperation
.newInsert(
android.provider.ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Contacts.Data.RAW_CONTACT_ID,
rawContactInsertIndex)
.withValue(ContactsContract.Contacts.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, contact.getPhone())
.withValue(ContactsContract.CommonDataKinds.Phone.TYPE,
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE)
.withValue(ContactsContract.CommonDataKinds.Phone.LABEL, "")
.withYieldAllowed(true)
.build());
}
// 真正添加
context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
}
public static void batchDeleteContact(Context context, List list)
throws RemoteException, OperationApplicationException {
ArrayList ops = new ArrayList<>();
for (FriendBean contact : list) {
String name = contact.getName();
//根据姓名求id
Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
ContentResolver resolver = context.getContentResolver();
Cursor cursor = resolver.query(uri, new String[]{ContactsContract.Data._ID},
"display_name=?", new String[]{name}, null);
if(cursor.moveToFirst()){
int id = cursor.getInt(0);
//根据id删除data中的相应数据
ops.add(ContentProviderOperation.newDelete(uri)
.withSelection("display_name=?",new String[]{name})
.build());
uri = Uri.parse("content://com.android.contacts/data");
ops.add(ContentProviderOperation.newDelete(uri)
.withSelection("raw_contact_id=?",new String[]{id + ""})
.build());
}
}
context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
}