Ormlite 查询需要 Android 的时间

Ormlite query takes time in Android

我在 Android 应用程序中工作,我在其中使用了 ormlite。我正在获取我的 phone 书籍联系人并将它们保存在我的本地数据库中,但问题是它花费了太多时间,例如将近 1500 个联系人需要将近 70 秒。

我在 ormlite 中搜索了批量插入,但我不知道如何在我的以下代码中实现它。

public static void loadLocalPhoneBookSample(Context ctx) {

        try{

        ContentResolver contentRes = ctx.getContentResolver();
        Cursor cur = null;

        String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER;
        cur = contentRes.query(ContactsContract.Contacts.CONTENT_URI, PROJECTIONS, selection, null, Phone.DISPLAY_NAME + " ASC");
        context = ctx;

        if (cur.getCount() > 0) {

            // create DB object
            MUrgencyDBHelper db = new MUrgencyDBHelper(ctx);
            RuntimeExceptionDao<ContactLocal, ?> contactDAO = db.getContactLocalIntDataDao();

            UpdateBuilder<ContactLocal, ?> updateDAO = contactDAO.updateBuilder();
            try {

                updateDAO.updateColumnValue("isUseless", true);
                updateDAO.update();
            } catch (SQLException e) {
                e.printStackTrace();
            }finally {
//              db.writeUnlock();
            }


            while (cur.moveToNext()) {
                String id = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));

                /** read names **/
                String displayName = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
                /** Phone Numbers **/

                Cursor pCur = contentRes.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID
                + " = ?", new String[] { id }, null);

                while (pCur.moveToNext()) {

                    String number = pCur
                            .getString(pCur
                                    .getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));

String formatedNo = number.replaceAll("\s+", "").replace("+", "00").replace("-", "").trim();

                        try {

                            QueryBuilder<ContactLocal, ?> query = contactDAO.queryBuilder();
                            query.where().eq("mFormatedNumber", number);

                            ContactLocal contact = query.queryForFirst();
                            boolean addContact = false, alreadyUpdated = true;

                            if (contact == null) {
                                addContact = true;
                                contact = new ContactLocal();
                                contact.setFirstName(displayName.trim());
                                contact.setLastName(displayName.trim());
                                contact.setContactNumber(formatedNo);
                            }

                            // check if this contact was already updated before
                            if (contact.getContactNumber() == null || contact.getContactNumber().length() == 0) {
                                contact.setContFirstLastNo(number, displayName, displayName, number);
                                alreadyUpdated = false;
                            }

                            contact.setUseless(false);

                            // if not updated already, Create/Update
                            if (addContact) {
                                contactDAO.create(contact);
                            } else
                                contactDAO.update(contact);
                        } 
                }

                pCur.close();
            }
        }
    }

这是我的修改版本(可能需要一些语法更改)

public static void loadLocalPhoneBookSample(Context ctx) {
try {
    ContentResolver contentRes = ctx.getContentResolver();
    String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER;
    Cursor cur = contentRes.query(ContactsContract.Contacts.CONTENT_URI, PROJECTIONS, selection, null, Phone.DISPLAY_NAME + " ASC");
    context = ctx;

    if (cur.getCount() > 0) {

        // create DB object
        MUrgencyDBHelper db = new MUrgencyDBHelper(ctx);
        RuntimeExceptionDao<ContactLocal, ?> contactDAO = db.getContactLocalIntDataDao();

        UpdateBuilder<ContactLocal, ?> updateDAO = contactDAO.updateBuilder();
        try {
            updateDAO.updateColumnValue("isUseless", true);
            updateDAO.update();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //              db.writeUnlock();
        }

        ArrayList<ContactLocal> contacts = new ArrayList<>();
        while (cur.moveToNext()) {
            String id = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));

            /** read names **/
            String displayName = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));

            /** Phone Numbers **/               
            Cursor pCur = contentRes.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?", new String[] { id }, null);
            while (pCur.moveToNext()) {

                String number = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                String formatedNo = number.replaceAll("\s+", "").replace("+", "00").replace("-", "").trim();

                try {
                    QueryBuilder<ContactLocal, ?> query = contactDAO.queryBuilder();
                    query.where().eq("mFormatedNumber", number);

                    ContactLocal contact = query.queryForFirst();

                    if (contact == null) {
                        contact = new ContactLocal();
                        contact.setFirstName(displayName.trim());
                        contact.setLastName(displayName.trim());
                        contact.setContactNumber(formatedNo);
                    }

                    contact.setUseless(false);
                    contacts.add(contact);
                } 
            }
            pCur.close();

        }
        contactDao.callBatchTasks(new Callable<Void>() {
            public Void call() throws Exception {
                for (ContactLocal contact : contacts) {
                    contactDAO.createOrUpdate(contact);
                }
            }
        });
    }
}

主要优化是使用callBatchTasks。来自 ormlite 文档:

Databases by default commit changes after every SQL operation. This method disables this "auto-commit" behavior so a number of changes can be made faster and then committed all at once.

通过创建一个 ArrayList 并跟踪更改,您可以一次性使用最后的 callBatchTasks 到 create/update。

我还注意到 alreadyUpdated 从未被访问过,因此删除是安全的。

另外 Dao 有一个 createOrUpdate 方法,它与您之前的 addContact if 语句相同。

the problem is that it is taking too much time like for almost 1500 contact it is taking almost 70 seconds

@CarloB 在 dao. callBatchTasks(...) 方法中进行质量创建方面有正确的答案。这是关于该主题的文档:

http://ormlite.com/docs/batch

为了加快速度,您还可以将所有 mFormatedNumber 记录在另一个 List 中,然后使用 IN 查询来查询它们。使用原始查询取回数据库中已有的 mFormatedNumber

results = dao.queryRaw(
    "SELECT mFormatedNumber from Contact WHERE mFormatedNumber IN ?",
    mFormatedNumberList);

要在 ORMLite 中使用原始查询,请参阅:

http://ormlite.com/docs/raw-queries

然后您将进行一次查询以查看需要创建哪些联系人,然后在批处理事务中执行所有插入操作。

否则,您正在执行约 3000 个同步数据库事务,不幸的是,在 Android 设备上每秒 40 个是非常典型的。