插入5000+安卓联系人的最有效方法



我意识到这在很多地方都有涉及,包括Stack Overflow,但我正在寻找人们可能使用过的任何其他解决方案。所以考虑到这一点。。。

我正在开发一个应用程序,用户可以在该应用程序中通过OTA同步所有联系人。这是通过一个web服务调用来完成的,该调用从服务器获取一组100个联系人,下载并解析信息,将联系人插入Android联系人数据库,确认收到这些联系人,然后对下一组100名联系人重复前面的步骤,直到同步完成。当用户有订单或1000-2000的联系人时,这个过程运行得很好,但这个应用程序的典型用户可以轻松地有5000-6000个联系人(超级用户有10000+),在这种情况下,所需时间远比我希望的要长。例如,一组大约5300个触点的样本可能需要大约13.5分钟才能完成。不错,但我希望它至少和iOS一样高效,如果可能的话,iOS在相同的数据集上运行大约8分钟。

我已经记录了每一步所需的时间,不出所料,瓶颈似乎是将数据插入Android合同数据库。在网上搜索后,我发现在插入数千个联系人方面几乎没有什么帮助,但我发现的似乎分为以下三组:

1) ContentProviderOperation——谷歌推荐的方式给了我13.5分钟的5300个联系人的基线。

2) 大容量插入--我读到builkInsert往往比applyBatch更高效,但当我自己尝试实现这一点时,同样的5300个联系人实际上需要25分钟。我有一种感觉,这在很大程度上是因为我需要插入RawContact信息,然后保存生成的URI,以便在创建ContactsContract.Data时使用。bulkInsert的数据更自然地通过ContentProviderOperation中的backValueReference获得。此外,我查看了android的源代码,并没有觉得bulkInsert非常高效。

3) 使用DatabaseUtils.InsertHelper和事务创建优化的大容量插入--不幸的是,这似乎是针对那些创建自己的内容提供商的人,因为您需要访问底层数据库作为实例变量,而我还没有看到如何使用本机联系人数据库来实现这一点。

有人有插入5000多个联系人的经验吗?或者我可以研究其他可能的想法来帮助减少我的时间吗?还是应该将ContentProviderOperation视为最佳化?

不幸的是,我认为1是最好的选择。我怀疑与iPhone相比,您的大部分开销都在内容提供商设计所固有的跨流程IPC中。

你对3的分析是正确的。

在根设备上有绕过内容提供商的选项,但我怀疑这是否是你想要的。

嗨,我在几分钟内插入了巨大的联系人,我的代码是:

public void insertContact(contactList:List<Contact>){
val queueSize = 300 //400
val contactQueue = contactList.size/queueSize
if(contactQueue > 0) {
var startIndex = 0
var endIndex = 0
var tempList: List<Request.ContactBean>? = null
totalQueue = contactQueue + 1+smsQueue
for (i in 0..contactQueue) {
startIndex = i * queueSize
endIndex = startIndex + queueSize
endIndex = if (endIndex < contactList.size) endIndex else contactList.size
tempList = contactList.subList(startIndex, endIndex);
Log.d(Constant.TAG_RESTORE, "In loop totalQueue: " + contactQueue + " i: " + i
+ " startIndex: " + startIndex + "endIndex: " + endIndex + " Queuesize: " + tempList.size)
restoreContact(tempList);
}
}else{
totalQueue = 1+smsQueue;
restoreContact(contactList);
}
}


private fun restoreContact( contactList: List<Request.ContactBean>) {

Observable.fromCallable { insertContact(contactList); }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
totalCompleteOperation++
if(totalCompleteOperation === totalQueue){
Log.d(Constant.TAG_RESTORE, " in subscribe restoreContact " +
"totalCompleteOperation: "+ totalCompleteOperation +" totalQueue "+totalQueue)
hideDialog();
completeRestore(true)
}
}
)
}

public void insertContact(List<Request.ContactBean> contacts) throws RemoteException, OperationApplicationException {
final int MAX_OPERATIONS_FOR_INSERTION = 100; //100
int size = contacts.size();
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
for (int i = 0; i < size; i++) {
createOperations(ops, contacts.get(i));
if (ops.size() >= MAX_OPERATIONS_FOR_INSERTION) {
mContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
ops.clear();
}
}
if (ops.size() > 0)
mContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); 
}

private void createOperations(ArrayList<ContentProviderOperation> ops,
Request.ContactBean contact){
int backReference = ops.size();
ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
.withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null)
.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null)
.withValue(ContactsContract.RawContacts.AGGREGATION_MODE, ContactsContract.RawContacts.AGGREGATION_MODE_DISABLED)
.build()
);
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
// .withYieldAllowed(true)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, backReference)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, contact.getName())
.build());
if (contact.getNumbers() != null && contact.getNumbers().size() > 0) {
// Adding insert operation to operations list
// to insert Mobile Number in the table ContactsContract.Data
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
//.withYieldAllowed(true)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, backReference)
.withValue(ContactsContract.Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
.withValue(Phone.NUMBER, contact.getNumbers().get(0).getNumber())
.withValue(Phone.TYPE, Phone.TYPE_MOBILE)
.build());
if (contact.getNumbers().size() > 1) {
// Adding insert operation to operations list
// to  insert Home Phone Number in the table ContactsContract.Data
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
//.withYieldAllowed(true)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, backReference)
.withValue(ContactsContract.Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
.withValue(Phone.NUMBER, contact.getNumbers().get(1).getNumber())
.withValue(Phone.TYPE, Phone.TYPE_HOME)
.build());
}
}
if (contact.getEmails() != null && contact.getEmails().size() > 0) {
// Adding insert operation to operations list
// to insert Work Email in the table ContactsContract.Data
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
// .withYieldAllowed(true)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, backReference)
.withValue(ContactsContract.Data.MIMETYPE, Email.CONTENT_ITEM_TYPE)
.withValue(Email.ADDRESS, contact.getEmails().get(0).getAddress())
.withValue(Email.TYPE, Email.TYPE_WORK)
.build());
}
if (contact.getEmails().size() > 1) {
// Adding insert operation to operations list
// to insert Home Email in the table ContactsContract.Data
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
// .withYieldAllowed(true)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, backReference)
.withValue(ContactsContract.Data.MIMETYPE, Email.CONTENT_ITEM_TYPE)
.withValue(Email.ADDRESS, contact.getEmails().get(1).getAddress())
.withValue(Email.TYPE, Email.TYPE_HOME)
.build());
}
}
This code will insert huge contact list in very less time.

最新更新