У меня была эта проблема за пару месяцев до этого, и теперь настало время вернуться к ней.
Я запрашиваю телефонный звонок в базу данных, но для заполнения таблицы требуется около 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();
Для каждого изменения, которое вы делаете в базе данных 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();
}
Вы вставляете во время просмотра, это делает блокировки в вашей базе данных. Вам нужно сначала закончить цикл while и только затем вставить его в свою базу данных. Просто сохраните данные в одном и том же временном связанном списке (лучше, чем массив в вашем случае, потому что он получил быстрые вставки)
Попробуйте использовать одну транзакцию для всего метода: http://notes.theorbis.net/2010/02/batch-insert-to-sqlite-on-android.html