插入 5000 多个 Android 联系人的最有效方式

Posted

技术标签:

【中文标题】插入 5000 多个 Android 联系人的最有效方式【英文标题】:Most Efficient Way to Insert 5000+ Android Contacts 【发布时间】:2012-11-14 12:40:21 【问题描述】:

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

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

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

1) ContentProviderOperation -- Google 推荐的方法,它为我提供了 13.5 分钟的基线,用于 5300 个联系人。

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

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

有没有人有插入 5000 多个联系人的经验或我可以考虑的任何其他可能的想法来帮助减少我的时间?还是应该将 ContentProviderOperation 视为已优化?

【问题讨论】:

如果您的唯一指标是“与 iOS 一样好”,您可能会发现这和其他任务是不可能的。由于不同的优化,每个平台都会有不同的优势。 1) vs 2) 很大程度上取决于实现细节。两者都只是捆绑了一堆操作,默认实现只会执行单个操作而不是事务。坚持最适合您的方式,您无法优化提供程序的内部实现 这可能是一个公平的回应。也许该指标应该比当前的 13.5 分钟更快。我完全理解有一点点苹果和橘子的比较,但是说 13 分钟似乎非常慢。尤其是考虑到用户拥有 10000 多个联系人的情况并不少见。 提供一些代码可能会有所帮助,因为有人可能会注意到您正在以缓慢的方式做某事。另外,您是否尝试过在不同的线程上运行下载和插入,看看是否有任何显着差异?出于好奇,5000 多个联系人的内存有多大?更多信息可能会帮助其他人帮助你:) 关于 bulkInsert,如何保存生成的原始联系人 URI?我在连接原始联系人表和数据表进行 2 次批量插入时遇到问题。 【参考方案1】:

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

你对 3 的分析是正确的。

有根设备上的选项可以绕过内容提供商,但我怀疑这就是您要寻找的。​​p>

【讨论】:

【参考方案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.

【讨论】:

以上是关于插入 5000 多个 Android 联系人的最有效方式的主要内容,如果未能解决你的问题,请参考以下文章

Android:插入/更新/删除联系人

Android:插入后获取联系人ID

每个程序员都应该读的最有影响力的一本书是啥? [关闭]

Android 以编程方式插入的联系人未链接到我的应用

如何使用意图和活动在 Xamarin Android 中插入城市、街道、邮政编码、国家/地区的联系人?

如何将多个表中的电话号码插入mysql中的单个联系人表?