日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android中向ContactsProvider中插入大量联系人

發布時間:2023/12/14 Android 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android中向ContactsProvider中插入大量联系人 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

應用場景

項目中存在這樣的需求,通過設備之間通過藍牙傳輸聯系人,并且需要將獲取過來的聯系人插入到ContactsProvider中

批量插入聯系人的標準代碼

在Android的源碼中,ContactsContract.java中為我們展示了批量插入聯系人的方法。

The batch method is by far preferred. It inserts the raw contact and itsconstituent data rows in a single database transactionand causes at most one aggregation pass.<pre>ArrayList<ContentProviderOperation> ops =new ArrayList<ContentProviderOperation>();...int rawContactInsertIndex = ops.size();ops.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI).withValue(RawContacts.ACCOUNT_TYPE, accountType).withValue(RawContacts.ACCOUNT_NAME, accountName).build());ops.add(ContentProviderOperation.newInsert(Data.CONTENT_URI).withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex).withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE).withValue(StructuredName.DISPLAY_NAME, &quot;Mike Sullivan&quot;).build());getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);

應用實例

在項目聯系人列表一開始是存在如下的一個本地聯系人列表中

public class TsContact implements Parcelable {public String mPhoneNumber;public int mPhoneType;public String mFirstName;public String mLastName;public String mMiddlename;... }

假如List < TsContact> contactsList中存放了聯系人列表,那么如何將這個TsContacts列表同步到ContactsProvider中呢?
我一開始采用的代碼如下:

private void syncTSContactsToContactsProvider(List<TsContact> contactsList) {ArrayList<ContentProviderOperation> ops = new ArrayList<>();for (int index = 0; index < contactsList.size(); index++) {TsContact contact = contactsList.get(index);int 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(ContactsContract.Data.CONTENT_URI).withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, rawContactInsertIndex).withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE).withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, contact.mLastName).withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, contact.mFirstName).withValue(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, contact.mMiddlename).withYieldAllowed(true).build());ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, rawContactInsertIndex).withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE).withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, contact.mPhoneNumber).withValue(ContactsContract.CommonDataKinds.Phone.TYPE, contact.mPhoneType).withValue(ContactsContract.CommonDataKinds.Phone.LABEL, "").withYieldAllowed(true).build());}try {getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);ops.clear();} catch (final TransactionTooLargeException e) {e.printStackTrace();} catch (final RemoteException e) {LogUtil.e(TAG, "RemoteException in commit");e.printStackTrace();} catch (final OperationApplicationException e) {LogUtil.e(TAG, "OperationApplicationException in commit");e.printStackTrace();} }

大功告成,運行成功,很開心。效率也比單個插入高很多。

遇到了TransactionTooLargeException

代碼運行了一段時間以后,測試提了一個bug,如果聯系人很多的時候,無法更新聯系人。
查了一下,當聯系人列表過大的時候,比如說超過一千,會拋出異常:TransactionTooLargeException。這是因為我們使用applyBatch接口來插入數據,最終還是需要通過binder將這些數據傳遞給ContactsProvider。而binder是輕量級跨進程通信機制,其傳遞數據上限。在Android 5.0中其定義為:
BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))
那么當聯系人列表過大,則傳遞的數據超過了binder所能傳遞數據的上限,拋出了異常。
怎么解決這個問題呢?那就是把聯系人列表截斷,分多次傳輸。

錯誤的分批批處理

一開始的我想到的是仍然將聯系人列表一次性轉化為插入操作的列表ArrayList< ContentProviderOperation > ops,再執行applyBatch,如果遇到TransactionTooLargeException,則將插入列表截成兩段,重新插入,如果還有異常,繼續截成兩段,就這樣二分下去,肯定可以插入 。恩,是的perfect code。

那么代碼就變成了

private void syncTSContactsToContactsProvider(List<TsContact> contactsList) {ArrayList<ContentProviderOperation> ops = new ArrayList<>();for (int index = 0; index < contactsList.size(); index++) {TsContact contact = contactsList.get(index);int 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(ContactsContract.Data.CONTENT_URI).withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, rawContactInsertIndex).withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE).withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, contact.mLastName).withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, contact.mFirstName).withValue(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, contact.mMiddlename).withYieldAllowed(true).build());ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, rawContactInsertIndex).withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE).withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, contact.mPhoneNumber).withValue(ContactsContract.CommonDataKinds.Phone.TYPE, contact.mPhoneType).withValue(ContactsContract.CommonDataKinds.Phone.LABEL, "").withYieldAllowed(true).build());}int transactionSize = ops.size();ArrayList<ContentProviderOperation> subOps = new ArrayList<>();while (!ops.isEmpty()) {subOps.clear();if (transactionSize > ops.size()) {transactionSize = ops.size();}subOps.addAll(ops.subList(0, transactionSize));try {getContentResolver().applyBatch(ContactsContract.AUTHORITY, subOps);ops.removeAll(subOps);} catch (final TransactionTooLargeException e) {// If the transaction is too large, try splitting it.if (transactionSize == 1) {Log.e(TAG, "Single operation transaction too large");}Log.d(TAG, "Transaction operation count %d too large, halving..." + transactionSize);transactionSize = transactionSize / 2;if (transactionSize < 1) {transactionSize = 1;}} catch (final RemoteException e) {Log.e(TAG, "RemoteException in commit");e.printStackTrace();} catch (final OperationApplicationException e) {Log.e(TAG, "OperationApplicationException in commit");e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();}}ops.clear(); }

恩,寫完很滿意,但是當ops被分成幾段,多次處理以后,只有第一次的插入是有效,后面的插入都是無效的了。

正確的批處理的插入姿勢

之所以ArrayList< ContentProviderOperation > ops被截斷 以后的插入,后面的插入都失敗了,是因為插入到id已經不對了,應該每次重新構建一個新的ContentProviderOperation列表。
所以正確的處理方式是:每次截取聯系人列表的一段,構建一個ContentProviderOperation列表,插入完成以后,取聯系人列表的下一段,再重新構建一個新的ContentProviderOperation列表,再次插入
所以正確的代碼是這樣的

private void syncTSContactsToContactsProvider(List<TsContact> contactsList) {final int contactsListSize = contactsList.size();int unitLength = 400; //large insert will cause binder data overflow.int syncedCount = 0;while (syncedCount < contactsListSize) {int syncLength = (contactsListSize - syncedCount) < unitLength ? (contactsListSize - syncedCount) : unitLength;ArrayList<ContentProviderOperation> ops = new ArrayList<>();for (int index = 0; index < contactsList.size(); index++) {TsContact contact = contactsList.get(index);int 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(ContactsContract.Data.CONTENT_URI).withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, rawContactInsertIndex).withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE).withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, contact.mLastName).withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, contact.mFirstName).withValue(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, contact.mMiddlename).withYieldAllowed(true).build());ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, rawContactInsertIndex).withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE).withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, contact.mPhoneNumber).withValue(ContactsContract.CommonDataKinds.Phone.TYPE, contact.mPhoneType).withValue(ContactsContract.CommonDataKinds.Phone.LABEL, "").withYieldAllowed(true).build());}try {getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);ops.clear();} catch (final TransactionTooLargeException e) {e.printStackTrace();} catch (final RemoteException e) {LogUtil.e(TAG, "RemoteException in commit");e.printStackTrace();} catch (final OperationApplicationException e) {LogUtil.e(TAG, "OperationApplicationException in commit");e.printStackTrace();}Log.d(TAG, "" + syncedCount + "contacts has been synced to contacts provider" );} }

總結

以上是生活随笔為你收集整理的Android中向ContactsProvider中插入大量联系人的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。