我意识到这已经在Stack Overflow上的各个地方有所涉及,包括here,但我正在寻找人们可能已经使用的任何其他解决方案。所以考虑到这一点...
我正在开发一个应用程序,用户最初可以将所有联系人与桌面应用程序OTA同步。这是通过Web服务调用完成的,该服务调用从服务器获取一组100个联系人,下载并解析信息,将联系人插入Android Contact DB,确认收到这些联系人,然后使用下一个步骤重复前面的步骤100个联系人,直到同步完成。当用户在订单或1000-2000上有联系人时,此过程很有效,但此应用程序的典型用户可以轻松拥有5000-6000个联系人(高级用户拥有超过10000个),在这种情况下,事情需要比我长得多我想。例如,大约5300个联系人的样本集可能需要大约13.5分钟才能完成。还不错,但我希望它至少和iOS一样有效,如果可能的话,对于相同的数据集运行大约8分钟。
我记录了每一步所花费的时间,不出所料,瓶颈似乎是将数据插入到Android合约数据库中。在浏览网页后,我找到了一些关于插入数千个联系人的帮助,但我发现的似乎属于这三组:
1)ContentProviderOperation - 谷歌推荐的方式,为5300个联系人提供了13.5分钟的基线。
2)批量插入 - 我读到builkInsert往往比applyBatch更有效,但是当我自己尝试实现这个时,它实际上需要25分钟才能使用相同的5300个联系人。我有一种感觉,这是因为我需要插入RawContact信息,然后保存生成的URI,以便为bulkInsert创建ContactsContract.Data,它更自然地通过ContentProviderOperation中的backValueReference来实现。另外,我查看了android源代码,我不觉得bulkInsert是非常有效的。
3)使用DatabaseUtils.InsertHelper和事务创建优化的批量插入 - 不幸的是,这似乎是针对那些创建自己的内容提供者的人,因为您需要访问底层数据库作为实例变量而且我还没有看看如何使用本机联系人DB。
有没有人有任何插入5000多个联系人或任何其他可能的想法的经验,以帮助减少我的时间?或者ContentProviderOperation应该被认为是最优化的吗?
答案 0 :(得分:0)
不幸的是,我相信1是最好的选择。我怀疑与iPhone相比,您的大部分开销都是内容提供商设计固有的跨流程IPC。
您对3的分析是正确的。
有根设备上有选项可以绕过内容提供商,但我怀疑这是你在寻找的。 p>
答案 1 :(得分:-2)
您好我在几分钟内插入了大量的联系人,我的代码是:
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.