Tengo una aplicación que ejecuta una actividad con un servicio en un proceso separado que se inicia y se vincula a la actividad. El servicio contiene un controlador que publica un ejecutable que se ejecutará después de un retraso."código de error 5: la base de datos está bloqueada" cuando se utiliza un ContentProvider
Quiero que cada componente se registre en la base de datos, así que implementé un proveedor de contenido que trata con el acceso a la base de datos y lo llamo desde el servicio o actividad a través de subclases AsyncTask ampliadas.
Todo esto funciona muy bien en el emulador, pero cuando lo ejecuto en depuración en mi teléfono me sale un error de bloqueo de base de datos esporádica en mi forma de escribir la base de datos:
ACTUALIZACIÓN
hice algunos cambios a mi manejo de la base de datos y el error ha cambiado ligeramente.
ERROR/Database(15235): Error inserting MY_MESSAGE
ERROR/Database(15235): android.database.sqlite.SQLiteException: error code 5: database is locked
ERROR/Database(15235): at android.database.sqlite.SQLiteStatement.native_execute(Native Method)
ERROR/Database(15235): at android.database.sqlite.SQLiteStatement.execute(SQLiteStatement.java:61)
ERROR/Database(15235): at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1591)
ERROR/Database(15235): at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1435)
ERROR/Database(15235): at mypackagename.DatabaseHelper.insertLogging(DatabaseHelper.java:190)
ERROR/Database(15235): at mypackagename.ContentProvider.insert(ContentProvider.java:139)
ERROR/Database(15235): at android.content.ContentProvider$Transport.insert(ContentProvider.java:198)
ERROR/Database(15235): at android.content.ContentResolver.insert(ContentResolver.java:604)
ERROR/Database(15235): at mypackagename.Activity$LogToDatabase.doInBackground(Activity.java:642)
ERROR/Database(15235): at mypackagename.Activity$LogToDatabase.doInBackground(Activity.java:1)
ERROR/Database(15235): at android.os.AsyncTask$2.call(AsyncTask.java:185)
ERROR/Database(15235): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306)
ERROR/Database(15235): at java.util.concurrent.FutureTask.run(FutureTask.java:138)
ERROR/Database(15235): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
ERROR/Database(15235): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
ERROR/Database(15235): at java.lang.Thread.run(Thread.java:1019)
no puse mucho detalle antes de porque pensé que había un problema con los diferentes procesos o hilos, pero ahora estoy pensando que el problema es más probable encuentran en el código llamando a la base de datos.
Preguntas:
1) ¿Por qué estoy golpeando cerraduras cuando estoy usando un ContentProvider?
2) ¿Por qué esto no aparece en un emulador API 2.3.3 equivalente?
3) ¿El hecho de que ninguno de mis códigos capte una excepción significa que el error se manejó correctamente y puedo ignorarlo?
4) Leí en otro lugar a alguien sugiriendo ajustar el tiempo de espera ocupado. ¿Como podría hacerlo?
La ironía de que es mi registro de depuración que está causando el error no se pierde en mí.
Si no puedo resolverlo, mi siguiente paso es agrupar los mensajes de registro en una lista y volcarlos en lotes de diez a la vez.
Aquí es el camino de la a través del código para el error:
Actividad:
private void logDatabaseMessage(String status, String message)
{
String[] args = {status, message};
LogToDatabase logTask = new LogToDatabase();
logTask.execute(args);
}
private class LogToDatabase extends AsyncTask<String, Integer, Void>
{
@Override
protected Void doInBackground(final String... args)
{
try
{
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
String dateText = dateFormat.format(new Date());
ContentValues loggingValues = new ContentValues();
loggingValues.put(MyContentProvider.LOGGING_DATETIME, dateText);
loggingValues.put(MyContentProvider.LOGGING_STATUS, args[0]);
loggingValues.put(MyContentProvider.LOGGING_MESSAGE, args[1]);
getContentResolver().insert(MyContentProvider.LOGGING_CONTENT_URI, loggingValues);
}
catch (Exception ex)
{
Log.e(TAG, "LogToDatabase.doInBackground threw exception: " + ex.getMessage());
ex.printStackTrace();
}
return null;
}
}
ContentProvider:
@Override
public Uri insert(Uri uri, ContentValues values)
{
Uri _uri = null;
long rowID = 0;
try
{
switch (uriMatcher.match(uri))
{
case LOGGING:
rowID = dbHelper.insertLogging(values);
if (rowID == 0)
throw new SQLException("Failed to insert row into " + uri);
_uri = ContentUris.withAppendedId(LOGGING_CONTENT_URI, rowID);
break;
default: throw new SQLException("Failed to insert row into " + uri);
}
if (rowID != 0)
getContext().getContentResolver().notifyChange(_uri, null);
}
catch (Exception ex)
{
Log.e(TAG, LogPrefix + "insert threw exception: " + ex.getMessage());
ex.printStackTrace();
}
return _uri;
}
DatabaseHelper:
public long insertLogging(ContentValues values)
{
long rowID = 0;
try
{
rowID = db.insert(LOGGING_TABLE, null, values);
}
catch (Exception ex)
{
Log.e(TAG, LogPrefix + "ERROR: Failed to insert into logging table: " + ex.getMessage());
ex.printStackTrace();
}
return rowID;
}
SQLiteDatabase y SQLiteOpenHelper aparecen solo en una de mis clases, que es miembro de mi proveedor de contenido, pero llamo al proveedor de contenido tanto del servicio como de la actividad. ¿Eso esta bien? – TomDestry
Eso está bien siempre y cuando solo tenga un SQLiteOpenHelper y su única instancia de SQLiteDatabase sea la que obtenga de SQLiteOpenHelper.getWritableDatabase() (o getReadableDatabase()). – dhaag23
@ dhaag23 Si quiero acceder al DB utilizando varios hilos, es decir, un hilo para escribir datos en el DB y otro para leer desde el mismo DB al mismo tiempo, ¿puedo usar la misma instancia de la clase Helper? – rahul