Android - заполнение sqlite таблицы занимает много времени

1

У меня была эта проблема за пару месяцев до этого, и теперь настало время вернуться к ней.

Я запрашиваю телефонный звонок в базу данных, но для заполнения таблицы требуется около 30 секунд. Похоже, что запрос занимает около 1 секунды, но население берет навсегда, хотя в телефоне хранятся только последние 500 вызовов. Почему это так медленно? Я делаю что-то неправильно?

Я тестирую его только на своем телефоне, так как у меня есть только 8 элементов в журнале вызовов эмулятора.

  final String[] projection = null;

            HotOrNot infoA = new HotOrNot(Charts.this);
            infoA.open();
            infoA.createtable_Calls();
            infoA.deleteAllEntries_Calls();
            infoA.close();

            final Context context = getApplicationContext();
            final String selection = null;
            final String sortOrder = android.provider.CallLog.Calls.DATE + " DESC";

            Cursor c = context.getContentResolver().query(android.provider.CallLog.Calls.CONTENT_URI, projection, selection, null, sortOrder);
            while (c.moveToNext()) { 
                String callLogID = c.getString(c.getColumnIndex(android.provider.CallLog.Calls._ID));

                int numberColumn = c.getColumnIndex(android.provider.CallLog.Calls.NUMBER);
                int dateColumn = c.getColumnIndex(android.provider.CallLog.Calls.DATE);
                int typeColumn = c.getColumnIndex(android.provider.CallLog.Calls.TYPE);
                int durationColumn = c.getColumnIndex(android.provider.CallLog.Calls.DURATION);
                int person = c.getColumnIndex(android.provider.CallLog.Calls.CACHED_NAME);



                String number = c.getString(numberColumn);
                int duration = c.getInt(durationColumn);
                String personname = c.getString(person);
                long callDate = c.getLong(dateColumn);
                int callType = c.getInt(typeColumn);

                if (duration >= 0)
                {
                    switch (callType) {
                    case 1:
                        duration_in = duration;
                        duration_out = 0;
                        break;
                    case 2:
                        duration_out = duration;
                        duration_in = 0;
                        break;
                    case 3:
                        duration_in = 0;
                        duration_out = 0;
                        break;


                    }
                    }

    //Here comes the slow part

                    HotOrNot info = new HotOrNot(Charts.this);
                    info.open();
                    info.pop   
ulate_Calls(personname, number, String.valueOf(callType), Integer.toString(duration), Long.toString(callDate), callLogID);
                info.close();   
             }

Это функция заполнения:

public long populate_Calls(String name, String phone, String type, String duration, String date, String contactid) {
        ContentValues cv = new ContentValues();
        cv.put(KEY_NAME, name);
        cv.put(KEY_PHONE, phone);
        cv.put(KEY_TYPE, type);
        cv.put(KEY_DURATION, duration);
        cv.put(KEY_DATE, date);
        cv.put(KEY_CONTACTID, contactid);
        return ourDatabase.insert(DATABASE_TABLE, null, cv);        
    }

EDIT:

К ответам Андреаса Ка и twaddington я модифицировал метод популяции в классе SQLiteOpenHelper, но, к сожалению, это не помогло:

public long populate_Calls(String name, String phone, String type, String duration, String date, String contactid) {
    ContentValues cv = new ContentValues();
     try {
         ourDatabase.beginTransaction();

         cv.put(KEY_NAME, name);
         cv.put(KEY_PHONE, phone);
         cv.put(KEY_TYPE, type);
         cv.put(KEY_DURATION, duration);
         cv.put(KEY_DATE, date);
         cv.put(KEY_CONTACTID, contactid);

         ourDatabase.yieldIfContendedSafely();

         ourDatabase.setTransactionSuccessful();
     } finally {
         ourDatabase.endTransaction();
     }

    return ourDatabase.insert(DATABASE_TABLE, null, cv);        
}

EDIT2: отправка всего кода на основе ответов Babibu и twaddington. Кстати, массивы temp_ теперь LinkedLists, но это не влияет на время.

 final String[] projection = null;
        final Context context = getApplicationContext();
        final String selection = null;
        final String sortOrder = android.provider.CallLog.Calls.DATE + " DESC";
        lv1 = (ListView) findViewById(R.id.ListView02); 


        HotOrNot infoA = new HotOrNot(Calllogs.this);
        infoA.open();
        infoA.createtable_Calls();
        infoA.deleteAllEntries_Calls();
        infoA.close();

          pd = ProgressDialog.show(Calllogs.this, "Please wait..", "Loading data, it may take a few" +
                " seconds based on the number of data.", false, true);

        Cursor c = context.getContentResolver().query(android.provider.CallLog.Calls.CONTENT_URI, projection, selection, null, sortOrder);
        while (c.moveToNext()) { 
            String callLogID = c.getString(c.getColumnIndex(android.provider.CallLog.Calls._ID));

            int numberColumn = c.getColumnIndex(android.provider.CallLog.Calls.NUMBER);
            int dateColumn = c.getColumnIndex(android.provider.CallLog.Calls.DATE);
            int typeColumn = c.getColumnIndex(android.provider.CallLog.Calls.TYPE);
            int durationColumn = c.getColumnIndex(android.provider.CallLog.Calls.DURATION);
            int person = c.getColumnIndex(android.provider.CallLog.Calls.CACHED_NAME);



            String number = c.getString(numberColumn);
            int duration = c.getInt(durationColumn);
            String personname = c.getString(person);
            long callDate = c.getLong(dateColumn);
            int callType = c.getInt(typeColumn);

            if (duration >= 0)
            {
                switch (callType) {
                case 1:
                    duration_in = duration;
                    duration_out = 0;
                    break;
                case 2:
                    duration_out = duration;
                    duration_in = 0;
                    break;
                case 3:
                    duration_in = 0;
                    duration_out = 0;
                    break;
                }
            }

            temp_name.add(personname);
            temp_num.add(number);
            temp_type.add(String.valueOf(callType));
            temp_dur.add(Integer.toString(duration));
            temp_date.add(String.valueOf(callDate));
            temp_id.add(callLogID);
          } //end of while loop


        HotOrNot infotemp = new HotOrNot(Calllogs.this);
        infotemp.open();


            for (int i=0; i<temp_name.size(); i++)
            {
                infotemp.populate_Calls(temp_name.get(i), temp_num.get(i), temp_type.get(i), temp_dur.get(i), temp_date.get(i), temp_type.get(i));
            }
 infotemp.close();

РЕШЕНИЕ

Я отправляю решение twaddington, которое сокращает время от 8 секунд до менее 2:

 HotOrNot infotemp = new HotOrNot(Calllogs.this);
        infotemp.open();

        // Get our database. You can do this however you wish, but
        // it seems like since the database is contained in your 'HotOrNot'
        // object, it would be best to simply add a getter method to
        // the class.
        SQLiteDatabase db = infotemp.getDatabase();

        try {
            // Begin our transaction
            db.beginTransaction();

            // Loop over the array of calls and
            // perform a db insert for each.
            for (int i=0; i<temp_name.size(); i++) {
                // Yield the database lock if requested. This will
                // temporarily suspend our loop, but it should
                // continue when the lock is opened.
                db.yieldIfContendedSafely();

                infotemp.populate_Calls(temp_name.get(i), temp_num.get(i),
                        temp_type.get(i), temp_dur.get(i), temp_date.get(i), temp_type.get(i));
            }

            // Mark our transaction as successful!
            db.setTransactionSuccessful();
        } finally {
            // Always end the transaction!
            db.endTransaction();
        }

        infotemp.close();
  • 1
    Используйте Traceview, чтобы определить точный источник вашей сложности.
  • 0
    Пожалуйста, рассчитайте для меня следующие времена: 'c.moveToNext ()', все время цикла while, 'infotemp.open ()', 'infotemp.populate_Calls', все время цикла
Показать ещё 1 комментарий
Теги:
achartengine

3 ответа

2
Лучший ответ

Для каждого изменения, которое вы делаете в базе данных SQLite, происходит ряд сложных шагов, в том числе создание файла журнала для отката изменения при возникновении ошибки. Вы можете обернуть свою серию обновлений в транзакции базы данных, чтобы заставить SQLite обрабатывать всю серию как одну операцию. Это будет намного более эффективно.

try {
    db.beginTransaction();
    while (c.moveToNext()) {
        // Yield the database lock if requested
        db.yieldIfContendedSafely();

        // Add your code here!
        // ...

        // Perform the database insert
        populate_Calls(...);
    }
    db.setTransactionSuccessful();
} finally {
    db.endTransaction();
}
  • 0
    Я отредактировал свой главный пост. Вы бы проверили, сделал ли я что-то не так? Я везде читал, что это ускоряет вставку, но в моем случае это не так
  • 0
    Вы должны поместить транзакцию вне цикла, который вызывает ваш метод populate. То, как вы сейчас это делаете, не делает ничего другого, все равно выполняет одну транзакцию для каждой вставки. То, что вы хотите, это одна транзакция для всех вставок. Если вы посмотрите приведенный выше пример, вы увидите, что транзакция начинается и заканчивается вне цикла.
Показать ещё 7 комментариев
1

Вы вставляете во время просмотра, это делает блокировки в вашей базе данных. Вам нужно сначала закончить цикл while и только затем вставить его в свою базу данных. Просто сохраните данные в одном и том же временном связанном списке (лучше, чем массив в вашем случае, потому что он получил быстрые вставки)

  • 0
    Он читает и пишет из разных баз данных. Вы правы, что это вообще плохая идея, но я не думаю, что это вызывает какие-то проблемы здесь. Скорее, основная проблема заключается в том, что ему нужно обернуть вставки в транзакцию.
  • 0
    @ twaddington те времена невозможны, он получил некоторую блокировку наверняка. А транзакция может стоить так много ... Я бы предложил разделить ее любым способом, даже если это просто для того, чтобы сфокусировать проблему. Кто знает, может быть, это ошибка Android ...
Показать ещё 6 комментариев
1

Попробуйте использовать одну транзакцию для всего метода: http://notes.theorbis.net/2010/02/batch-insert-to-sqlite-on-android.html

Ещё вопросы

Сообщество Overcoder
Наверх
Меню