插入5000个以上的安卓联系人的最有效方法是什么?

8
我知道这个问题在各个地方都有讨论,包括Stack Overflow上的这里,但我正在寻找人们可能使用的其他解决方案。 因此...

我正在开发一个应用程序,用户可以通过OTA与桌面应用程序同步所有联系人。 这是通过Web服务调用完成的,该调用从服务器获取一组100个联系人,下载和解析信息,将联系人插入Android Contact DB中,确认接收这些联系人,然后重复前面的步骤以获取下一组100个联系人,直到同步完成。 当用户有1000-2000个联系人时,此过程效果很好,但是此应用程序的典型用户可以轻松拥有5000-6000个联系人(超级用户有10000多个),在这种情况下,事情比我想象的要慢得多。 例如,大约5300个联系人的样本集合需要约13.5分钟才能完成。 不错,但如果可能的话,我希望它至少与运行相同数据集的iOS一样高效,大约为8分钟。

我记录了每个步骤所需的时间,毫不奇怪的是,瓶颈似乎在于将数据插入Android contract DB中。 在搜索网络后,我发现在插入数千个联系人方面帮助很少,但我找到的似乎分为以下三组:

1)ContentProviderOperation --这是Google推荐的方式,为5300个联系人提供了13.5分钟的基准。

2)批量插入 --我读到builkInsert tend比applyBatch更有效,但当我尝试自己实现时,同样的5300个联系人实际上需要25分钟。 我觉得这很大程度上是因为我需要插入RawContact信息,然后保存用于创建ContactsContract.Data的结果URI,该URI通过ContentProviderOperation中的backValueReference更自然地生成。 此外,我查看了android源代码,我不认为bulkInsert非常高效。

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

是否有人有插入5000多个联系人的经验或任何其他可能的想法,可以帮助减少我的时间? 或者ContentProviderOperation应该被认为是它将要达到的最佳状态?


如果你的唯一指标是“和iOS一样好”,那么你可能会发现这个任务以及其他任务都是不可能完成的。由于不同的优化,每个平台都会有不同的优势。 - Kyeotic
这可能是一个公平的回应。也许指标应该只是比当前的13.5分钟更快。我完全理解这有点不可比较,但话说回来,13分钟似乎非常慢。特别是考虑到用户通常拥有10000多个联系人这一事实。 - prestona
十年后,现在是否有更好的方法来做事情呢? - Chagai Friedlander
@ChagaiFriedlander 我没有找到更好的方法来做这件事,尽管 Android 的支持有限,所以我们从未重新审视过。我认为最大的帮手是更新的硬件和网络速度。 - prestona
好的,谢谢!我也遇到了崩溃的问题... - Chagai Friedlander
显示剩余3条评论
2个回答

0

很遗憾,我认为选项1是最好的选择。我怀疑与iPhone相比,你的大部分开销都在内容提供者设计中固有的跨进程IPC上。

你对选项3的分析是正确的。

在已root的设备上有绕过内容提供者的选项,但我不认为这是你要寻找的。


-1

你好,我在几分钟内插入了大量联系人,我的代码如下:

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.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接