优化数以千计的 SQLite 查询 android

Posted

技术标签:

【中文标题】优化数以千计的 SQLite 查询 android【英文标题】:Optimise thousands of SQLite queries android 【发布时间】:2018-10-29 20:59:04 【问题描述】:

我有这个任务,我需要查询每个联系人的所有详细信息并进行备份。

前 770 个联系人需要 5-6 秒才能加载,而在 770 个联系人之后,每个联系人需要 1 秒以上来查询所有数据。我知道这是一个复杂的查询,但这太慢了。有什么办法可以优化吗?

代码

// This contacts array typically contains more than a thousand items in it, even more
contacts.forEach 
val lookupKey = it.lookupKey
queryContacts(lookupKey)


fun queryContacts(lookupKey: String) 
    val contact = Contact()
    val contactsCursor = resolver.query(CONTENT_URI, arrayOf(_ID, DISPLAY_NAME, HAS_PHONE_NUMBER), CONTACTS_ALL_DETAILS_SELECTION, arrayOf(lookupKey), null)

    if (contactsCursor.count > 0) 

        while (contactsCursor.moveToNext()) 

            val contactId = contactsCursor.getString(contactsCursor.getColumnIndex(_ID))
            val name = contactsCursor.getString(contactsCursor.getColumnIndex(DISPLAY_NAME))
            contact.name = name

            val hasPhoneNumber = Integer.parseInt(contactsCursor.getString(contactsCursor.getColumnIndex(HAS_PHONE_NUMBER)))

            // This is to read all phone numbers associated with the contact
            val phoneNumbers = arrayListOf<String>()
            if (hasPhoneNumber > 0) 
                val phoneCursor = contentResolver.query(PHONE_CONTENT_URI, null, "$PHONE_CONTACT_ID = ?", arrayOf(contactId), null)
                while (phoneCursor.moveToNext()) 
                    phoneNumbers.add(phoneCursor.getString(phoneCursor.getColumnIndex(NUMBER)))
                
                phoneCursor.close()
            
            contact.phoneNumbers.addAll(phoneNumbers)

            // Read every email id associated with the contact
            val emailCursor = contentResolver.query(EMAIL_CONTENT_URI, null, "$EMAIL_CONTACT_ID = ?", arrayOf(contactId), null)

            val emailIds = arrayListOf<String>()
            while (emailCursor.moveToNext()) 

                val email = emailCursor.getString(emailCursor.getColumnIndex(DATA))
                emailIds.add(email)
            
            emailCursor.close()
            contact.emails.addAll(emailIds)

            val columns = arrayOf(
                    ContactsContract.CommonDataKinds.Event.START_DATE,
                    ContactsContract.CommonDataKinds.Event.TYPE,
                    ContactsContract.CommonDataKinds.Event.MIMETYPE)

            val where = ContactsContract.CommonDataKinds.Event.TYPE + "=" + ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY +
                    " and " + ContactsContract.CommonDataKinds.Event.MIMETYPE + " = '" + ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE + "' and " + ContactsContract.Data.CONTACT_ID + " = " + contactId

            val selectionArgs = arrayOf<String>()
            val sortOrder = ContactsContract.Contacts.DISPLAY_NAME

            val birthdayCur = contentResolver.query(ContactsContract.Data.CONTENT_URI, columns, where, selectionArgs, sortOrder);
            val birthayList = arrayListOf<String>()
            if (birthdayCur.count > 0) 
                while (birthdayCur.moveToNext()) 
                    val birthday = birthdayCur.getString(birthdayCur.getColumnIndex(ContactsContract.CommonDataKinds.Event.START_DATE))
                    birthayList.add(birthday)
                
            
            birthdayCur.close()
            contact.dates.addAll(birthayList)
        

    

    contactsToBackup.add(contact)
    contactsCursor.close()


如您所见,整个复杂的查询被调用了 1000 多次。我该如何改进?

【问题讨论】:

这是否可以优化部分取决于备份的存储位置和方式。示例:如果联系人数据的备份存储在 Web 服务器上的 mysql 数据库中,您可以将整个 SQLite 文件加载到服务器并让服务器处理它。另一种选择是简单地在 SQLite 数据库的联系人表中添加一个标志列,指示联系人数据是否已更改并需要备份。 没有我的服务的那部分是优化的。我需要优化 android 公开的 Contacts Provider 的查询速度。在这里,我正在阅读我们手机上的联系人。 【参考方案1】:

您实际上并不需要optimise thousands of SQLite queries,您需要的是将数千个查询迁移到一个大查询中。

我假设这部分:

contacts.forEach 
val lookupKey = it.lookupKey
queryContacts(lookupKey)

实际上会遍历设备上的所有个联系人,如果是这样,您可以这样做:

Map<Long, Contact> contacts = new HashMap<>();

String[] projection = Data.CONTACT_ID, Data.DISPLAY_NAME, Data.MIMETYPE, Data.DATA1, Data.DATA2, Data.DATA3;
String selection = Data.MIMETYPE + " IN ('" + Phone.CONTENT_ITEM_TYPE + "', '" + Email.CONTENT_ITEM_TYPE + "', '" + Event.CONTENT_ITEM_TYPE + "')";
Cursor cur = cr.query(Data.CONTENT_URI, projection, selection, null, null);

while (cur != null && cur.moveToNext()) 
    long id = cur.getLong(0);
    String name = cur.getString(1);
    String mime = cur.getString(2); // email / phone / event
    String data = cur.getString(3); // the actual info, e.g. +1-212-555-1234
    int type = cur.getInt(4);

    Log.d(TAG, "got " + id + ", " + name + ", " + data);

    Contact contact;
    if (contacts.containsKey(id)) 
        contact = contacts.get(id);
     else 
        contact = new Contact();
        contact.name = name;
        contacts.put(id, infos);
    

    switch (mime) 
        case Phone.CONTENT_ITEM_TYPE: 
            contact.addPhone(data, type); // you'll need to add this method
            break;
        case Email.CONTENT_ITEM_TYPE: 
            contact.addEmail(data, type); // you'll need to add this method
            break;
        case Event.CONTENT_ITEM_TYPE: 
            contact.addBirthday(data, type); // you'll need to add this method
            break;
    

【讨论】:

以上是关于优化数以千计的 SQLite 查询 android的主要内容,如果未能解决你的问题,请参考以下文章

SQLite:如何优化 UNION 查询

Android数据库代码优化 - 从SQLite说起

如果可能的话,优化一个简单的 SQLite 查询!

优化对只读sqlite数据库的快速访问?

sqlite 查询优化

SQLite3 数据库查询优化