2012-10-19 3 views
5

Sto usando SQLiteOpenHelper per scrivere e leggere dal database SQlite su Android. Quando l'utente fa clic sull'interfaccia utente, leggo dal database SQLite utilizzando AsyncTask ma al momento esatto in cui sto aggiornando e scrivendo nel database in background usando altri AsyncTask.Accesso multithread Android SQlite

Ogni x volte ricevo un'eccezione bloccata nel database. Come posso risolvere questo? Si può accedere a SQlite in qualche modo da più thread contemporaneamente?

Lo sto usando in questo modo: ho una classe Database che si estende da SQLiteOpenHelper. Ho implementato i metodi OnCreate e ONUPGRADE e ogni volta che sto leggendo da database o scrivendo a base di dati che uso SQLiteDatabase così:

SQLiteDatabase database = null; 
try { 

    database = new Database(context).getWritableDatabase(); 

    .... 
    writing and reading from database... 
    .... 

} catch (Exception e) { 
    e.printStackTrace(); 
} finally { 
    if (database != null) { 
     database.close(); 
    } 
} 

Alla fine ho anche vicino SQLiteStatements e cursori se li uso. Dovrei usare @Justin answer e avere un singleton della classe database in Application?

Questo è l'errore che ottengo:

E/SqliteDatabaseCpp(17377): sqlite3_open_v2("/data/data/com.skulptur/databases/LGM.s3db", &handle, 6, NULL) failed 
E/SQLiteDatabase(17377): Failed to open the database. closing it. 
E/SQLiteDatabase(17377):  android.database.sqlite.SQLiteDatabaseLockedException: database is locked 
E/SQLiteDatabase(17377): at android.database.sqlite.SQLiteDatabase.dbopen(Native Method) 
E/SQLiteDatabase(17377): at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:983) 
E/SQLiteDatabase(17377): at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:956) 
E/SQLiteDatabase(17377): at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:1021) 
E/SQLiteDatabase(17377): at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:750) 
E/SQLiteDatabase(17377): at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:221) 
E/SQLiteDatabase(17377): at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:149) 
E/SQLiteDatabase(17377): at android.database.sqlite.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:223) 
+0

Penso che tutto ciò che si può fare con le librerie Android su sqllite sia sincronizzato. Non puoi provare a catturare l'eccezione e riprovare se ottieni lo stato di blocco? – quinestor

+1

Puoi modificare il tuo post e fornire il primo paio di righe dell'eccezione e di eventuali dichiarazioni di causa? Ho il sospetto che questo sia meno di ciò che stai facendo e di più sul riutilizzo di un database chiuso o qualcosa del genere. – Gray

risposta

7

Questo è quello che faccio per evitare problemi di blocco .. Lascio che la mia applicazione gestisca una singola istanza del mio db helper e mi riferisca sempre ad essa per ottenere un handle sul mio DB.

public class MyApp extends Application { 
    // My instance of SQLiteOpenHelper 
    public static DbHelper openHelper; 

    @Override 
    public void onCreate() { 
     super.onCreate(); 
     if (openHelper == null) { 
      openHelper = new DbHelper(this); 
     } 
    } 

    public synchronized static SQLiteDatabase getDB() { 
     return openHelper.getWritableDatabase(); 
    } 
} 

Una volta fatto questo, ogni volta che si vuole fare una query db, ottenere l'handle db come MyApp.getDB() e si avrà mai un problema di bloccaggio. Assicurati di non chiudere MAI il DB. Questo modello mantiene una singola connessione in qualsiasi momento nella tua app. SQLite è destinata ad essere sincrona su Android, quindi se si hanno a lungo i processi in esecuzione DB, come un 1000 inserti in una singola transazione ti consigliamo di codice in questo modo:

db.beginTransaction(); 
try { 
    for (DBRow row : insertList) { 
     // your insert code 
     insertRow(row); 
     db.yieldIfContendedSafely(); 
    } 
    db.setTransactionSuccessful(); 
} finally { 
    db.endTransaction(); 
} 

Questo permetterà altre query per interject se stessi in modo che non vedrai la tua app bloccarsi durante gli aggiornamenti in background.

+2

puoi rendere il tuo 'SQLiteOpenHelper' un singleton meglio di quello che sovrascrive la classe' Application'. – kdehairy

+0

sì, mi sono reso conto che dopo aver scritto questo: - \ .. non mi sentivo di cambiare la mia risposta – JustinMorris

+6

Ho paura che ti sbagli sulla tua ipotesi di non avere mai un problema di blocco: l'unica cosa che stai sincronizzando è il tuo accesso al metodo 'getWritableDatabase', che significa: solo * questo * non verrà mai chiamato simultaneamente, qualunque cosa accada con l'oggetto' SQLiteDatabase' che restituisce, è * non sincronizzato *. – theV0ID

1

Ci sono poche cose da provare. Prima prova a passare la stessa istanza di SQLiteOpenHelper a tutte le tue attività. Secondo utilizza startTransaction e endTransaction sul tuo SQLiteDatabase all'interno del tuo Helper. Terzo è l'aggiunta di synchronized a tutti i tuoi methonds di aiuto utilizzati nelle attività.

+0

la tua strada va bene, ma preferisco ContentProvider, mi dà la separazione tra app e db. Puoi anche considerare ORMLite o un'altra lib di tipo ORM, ce ne sono molti, un buon esempio è CROM quello che ho fatto qualche tempo fa https: // github.com/rysiekblah/crom –

0

Penso che si stiano utilizzando le connessioni del database in modo errato. Può essere causato da cursori non chiusi o altro. L'implementazione del database Sqlite è thread-safe ed è possibile accedervi contemporaneamente da diversi thread. Fornisci maggiori dettagli sul tuo problema (traccia dello stack di eccezioni, esempio di codice che causa un'eccezione, ecc.) In modo che possiamo aiutarti.