ContentProviderOperation批量操作提升性能

原文出处:http://blog.csdn.net/imuhao/article/details/52002995

ContentProviders 是Android 系统核心组件之一,ContentProviders 封装了数据的访问接口,其底层数据一般都是保存在数据库中或者保存在云端。

有时候你需要更新多行数据,可以选择调用多次ContentResolver的对应函数,或者 使用批量操作。当然 后者性能会比较好些。

为了使批量更新、插入、删除数据更加方便,android系统引入了 ContentProviderOperation类。

在官方开发文档中推荐使用ContentProviderOperations,有一下原因:

  • 所有的操作都在一个事务中执行,这样可以保证数据完整性
  • 由于批量操作在一个事务中执行,只需要打开和关闭一个事务,比多次打开关闭多个事务性能要好些
  • 使用批量操作和多次单个操作相比,减少了应用和ContentProvider之间的上下文切换,这样也会提升应用的性能,并且减少占用CPU的时间,当然也会减少电量的消耗。

要创建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)。

Builder对象核心函数

  • 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);
    }

你可能感兴趣的:(Android性能优化)