móviles - manual de programacion android pdf
Base de datos SQLite de Android: inserción lenta (5)
Necesito analizar un archivo XML bastante grande (que varía entre aproximadamente cien kilobytes y varios cientos de kilobytes), lo cual estoy haciendo usando Xml#parse(String, ContentHandler)
. Actualmente estoy probando esto con un archivo de 152 KB.
Durante el análisis, también inserto los datos en una base de datos SQLite utilizando llamadas similares a las siguientes: getWritableDatabase().insert(TABLE_NAME, "_id", values)
. Todo esto en conjunto toma alrededor de 80 segundos para el archivo de prueba de 152 KB (que se reduce a la inserción de aproximadamente 200 filas).
Cuando comento todas las instrucciones de inserción (pero dejo todo lo demás, como crear ContentValues
etc.), el mismo archivo solo demora 23 segundos.
¿Es normal que las operaciones de la base de datos tengan una sobrecarga tan grande? ¿Puedo hacer algo al respecto?
Dado que el InsertHelper mencionado por Yuku y Brett está en deprecated ahora (API nivel 17), parece que la alternativa correcta recomendada por Google es el uso de SQLiteStatement .
Usé el método de inserción de base de datos así:
database.insert(table, null, values);
Después de que también experimenté algunos problemas graves de rendimiento, el siguiente código aceleró mis 500 insertos de 14.5 segundos a solo 270 ms , ¡increíble!
Aquí es cómo utilicé SQLiteStatement:
private void insertTestData() {
String sql = "insert into producttable (name, description, price, stock_available) values (?, ?, ?, ?);";
dbHandler.getWritableDatabase();
database.beginTransaction();
SQLiteStatement stmt = database.compileStatement(sql);
for (int i = 0; i < NUMBER_OF_ROWS; i++) {
//generate some values
stmt.bindString(1, randomName);
stmt.bindString(2, randomDescription);
stmt.bindDouble(3, randomPrice);
stmt.bindLong(4, randomNumber);
long entryID = stmt.executeInsert();
stmt.clearBindings();
}
database.setTransactionSuccessful();
database.endTransaction();
dbHandler.close();
}
Deberías hacer inserciones por lotes.
Pseudocódigo:
db.beginTransaction();
for (entry : listOfEntries) {
db.insert(entry);
}
db.setTransactionSuccessful();
db.endTransaction();
Eso incrementó la velocidad de las inserciones en mis aplicaciones extremadamente.
Actualizar:
@Yuku proporcionó una publicación de blog muy interesante: Android utilizando inserthelper para realizar inserciones más rápidas en la base de datos sqlite
La compilación de la instrucción de inserción sql ayuda a acelerar las cosas. También puede requerir más esfuerzo para apuntalar todo y evitar una posible inyección, ya que ahora todo recae sobre sus hombros.
Otro enfoque que también puede acelerar las cosas es la clase de base de datos android.database.DatabaseUtils.InsertHelper. Mi entendimiento es que realmente envuelve las declaraciones de compilación insertadas. Pasar de las inserciones transaccionadas no compiladas a las inserciones transaccionadas compiladas fue aproximadamente una ganancia de velocidad de 3x (2ms por inserción a .6ms por inserción) para mis inserciones grandes (200K + entradas) pero simples de SQLite.
Código de muestra:
SQLiteDatabse db = getWriteableDatabase();
//use the db you would normally use for db.insert, and the "table_name"
//is the same one you would use in db.insert()
InsertHelper iHelp = new InsertHelper(db, "table_name");
//Get the indices you need to bind data to
//Similar to Cursor.getColumnIndex("col_name");
int first_index = iHelp.getColumnIndex("first");
int last_index = iHelp.getColumnIndex("last");
try
{
db.beginTransaction();
for(int i=0 ; i<num_things ; ++i)
{
//need to tell the helper you are inserting (rather than replacing)
iHelp.prepareForInsert();
//do the equivalent of ContentValues.put("field","value") here
iHelp.bind(first_index, thing_1);
iHelp.bind(last_index, thing_2);
//the db.insert() equilvalent
iHelp.execute();
}
db.setTransactionSuccessful();
}
finally
{
db.endTransaction();
}
db.close();
Si la tabla tiene un índice, considere descartarla antes de insertar los registros y luego volver a agregarla después de que haya confirmado sus registros.
Si usa un ContentProvider:
@Override
public int bulkInsert(Uri uri, ContentValues[] bulkinsertvalues) {
int QueryType = sUriMatcher.match(uri);
int returnValue=0;
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
switch (QueryType) {
case SOME_URI_IM_LOOKING_FOR: //replace this with your real URI
db.beginTransaction();
for (int i = 0; i < bulkinsertvalues.length; i++) {
//get an individual result from the array of ContentValues
ContentValues values = bulkinsertvalues[i];
//insert this record into the local SQLite database using a private function you create, "insertIndividualRecord" (replace with a better function name)
insertIndividualRecord(uri, values);
}
db.setTransactionSuccessful();
db.endTransaction();
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
return returnValue;
}
Luego, la función privada para realizar la inserción (aún dentro de su proveedor de contenido):
private Uri insertIndividualRecord(Uri uri, ContentValues values){
//see content provider documentation if this is confusing
if (sUriMatcher.match(uri) != THE_CONSTANT_IM_LOOKING_FOR) {
throw new IllegalArgumentException("Unknown URI " + uri);
}
//example validation if you have a field called "name" in your database
if (values.containsKey(YOUR_CONSTANT_FOR_NAME) == false) {
values.put(YOUR_CONSTANT_FOR_NAME, "");
}
//******add all your other validations
//**********
//time to insert records into your local SQLite database
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
long rowId = db.insert(YOUR_TABLE_NAME, null, values);
if (rowId > 0) {
Uri myUri = ContentUris.withAppendedId(MY_INSERT_URI, rowId);
getContext().getContentResolver().notifyChange(myUri, null);
return myUri;
}
throw new SQLException("Failed to insert row into " + uri);
}