2011-12-02 15 views
5

Ho un'applicazione che esegue un'attività con un servizio in un processo separato avviato e associato all'attività. Il servizio contiene un gestore che registra un eseguibile da eseguire dopo un ritardo."codice di errore 5: database bloccato" quando si utilizza ContentProvider

Desidero che ogni componente effettui il log nel database, quindi ho implementato un fornitore di contenuti che si occupa dell'accesso al database e lo chiamo dal servizio o dall'attività tramite sottoclassi AsyncTask estese.

Questo tutto funziona splendidamente su l'emulatore, ma quando l'eseguo in di debug sul mio telefono ottengo un errore di bloccaggio base di dati sporadici sulla mia scrittura database:

UPDATE

ho fatto alcune modifiche al la gestione del mio database e l'errore sono leggermente cambiati.

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) 

non ho messo molti dettagli prima nella perché ho pensato che fosse un problema con i differenti processi o thread, ma ora sto pensando che il problema è più probabile trova nel codice chiamando il database.

Domande:

1) Perché sto colpendo le serrature quando sto usando un ContentProvider?
2) Perché questo non viene visualizzato su un emulatore API 2.3.3 equivalente?
3) Il fatto che nessuno del mio codice rilevi un'eccezione significa che l'errore è stato gestito correttamente e posso ignorarlo?
4) Ho letto in un altro punto qualcuno che suggerisce di regolare il timeout occupato. Come potrei farlo?

L'ironia del fatto che si tratta della registrazione di debug che causa l'errore non viene persa.

Se non riesco a risolverlo, il mio prossimo passo è raggruppare i messaggi di registrazione in un elenco e scaricarli in gruppi di dieci alla volta.

Ecco il percorso attraverso il codice per l'errore:

attività:

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; 
} 

risposta

13

Sei forse accedere al database utilizzando più istanze SQLiteDatabase (o forse SQLiteOpenHelper)?

Puoi avere una sola connessione al database, altrimenti otterrai gli errori che hai riscontrato.

SQLiteDatabase è thread-safe, quindi è possibile accedervi contemporaneamente.

+0

SQLiteDatabase e SQLiteOpenHelper compaiono solo in una delle mie classi, che è un membro nel mio provider di contenuti, ma chiamo il fornitore di contenuti sia dal servizio che dall'attività. È ok? – TomDestry

+0

Questo va bene finché si ha solo uno SQLiteOpenHelper e l'unica istanza di SQLiteDatabase è quella che si ottiene da SQLiteOpenHelper.getWritableDatabase() (o getReadableDatabase()). – dhaag23

+5

@ dhaag23 Se voglio accedere al DB utilizzando più thread, cioè un thread per scrivere dati nel DB e altri per leggere contemporaneamente dallo stesso DB, Hw posso usare la stessa istanza della classe Helper? – rahul

0

Controllare anche il flag multiprocess del provider di contenuti nel manifest. Ogni processo avrebbe una connessione separata al database.

Problemi correlati