2011-04-07 18 views
11

Nella mia app devo implementare alcuni servizi di interfaccia utente e sincronizzazione. Viene eseguito in background e aggiorna i dati. Il servizio di sincronizzazione non è molto semplice, utilizza il multithreading.E riguardo il multithreading in Android SQLite?

Quindi, ecco la mia storia: Quando ho iniziato a sviluppare questa app non sapevo nulla di sqlite, quindi non ho usato alcuna sincronizzazione dei thread in Java. Risultato: ho ricevuto molte eccezioni come "SQLiteException: database bloccato: BEGIN EXCLUSIVE;"

Quindi ho sincronizzato tutte le mie transazioni con il normale blocco Java sincronizzato() {}. Tutto è andato molto meglio. Ma ho usato i cursori per l'implementazione di CursorAdapter per le mie liste. Così, a volte stavo ottenendo lo stesso "SQLiteException: database è bloccato: BEGIN EXCLUSIVE;"

Ho terminato la creazione di un'utilità sqlite sicura per piccoli thread che gestisce tutta la roba thread-safe. Inoltre devo usare qualcosa come ArrayAdapter (leggi tutti i dati dal Cursore e chiudilo dopo aver letto, sincronizza anche questo blocco) per la mia interfaccia utente. Quindi, funziona OK

Ma, non mi piace tale modo di gestire l'interfaccia utente, perché l'interfaccia utente è molto più lento con questa soluzione: leggere una certa quantità di dati dal cursore è piuttosto veloce, ma è più lento rispetto all'utilizzo di CursorAdapter

Quindi, chi ha trovato la soluzione per questa domanda? Grazie

+0

In che senso l'interfaccia utente ha più lento? Ci vuole più tempo per rispondere a tocchi ecc, o solo più tempo per visualizzare i dati del database? Se stai leggendo il tuo database allora non usare le transazioni - questo ti permette di eseguire più letture dal DB in una sola volta. –

+0

@Joseph Earl Intendevo "più tempo per visualizzare i dati". e sì, non uso le transazioni quando leggo i dati. Li uso solo quando scrivo alcuni dati –

risposta

16

Quindi, finalmente è venuto fuori la soluzione. Ecco qui.

Ho letto alcuni forum, gruppi di google e ho scoperto che il database SQLite deve essere aperto solo una volta. Quindi, l'ho implementato usando singleton.

Inoltre, ho implementato del codice db per sincronizzare tutte le operazioni di scrittura (per evitare che molti thread eseguissero operazioni di scrittura contemporaneamente). E non mi interessa aprire i cursori, leggendo da loro.

Dopo alcune prove giorni non c'ho le segnalazioni di errori da parte dei miei utenti, quindi penso che questo funziona

Nel mio lavoro precedente ho aperto database SQLite molte volte in tutta l'applicazione, che era il problema.

+0

Quindi ... se apri il database solo una volta ...come sbarazzarsi di questo fastidioso avviso: 'close() non è mai stato chiamato esplicitamente sul database '/ data/data/com.package/databases/name.db' android.database.sqlite.DatabaseObjectNotClosedException: L'applicazione non ha chiuso il cursore o oggetto di database che è stato aperto qui' – Cristian

+0

@evgeny di una singola tonnellata, la lettura è consentita mentre la scrittura è in corso. Ho usato singleton nella mia app l'interfaccia utente risponde molto lentamente, mentre legge i dati quando la scrittura è in corso. – Kishore

+0

@Kishore puoi leggere i dati ogni volta che vuoi. inoltre ... non leggere i dati nella discussione principale. Dovresti sempre leggere i dati nel thread di background –

5

SQLite implementa un blocco di scrittura esclusivo, modello di blocco di lettura condiviso. Ciò significa che è possibile avere contemporaneamente lettori concorrenti attivi nel database o un singolo writer, non è possibile avere entrambi. Se si utilizza la funzionalità di registrazione WAL, è possibile avere contemporaneamente un singolo writer e più lettori attivi nel database, ma non è possibile avere più di uno scrittore. Vi è un'eccellente documentazione sulla concorrenza SQLite here e here.

Si potrebbe prendere in considerazione di dare un'occhiata a Berkeley DB. Berkeley DB offre un SQL API completamente compatibile con SQLite. In realtà, ciò che abbiamo fatto è aggiungere il parser SQLite, il pianificatore e l'esecutore in cima al livello di archiviazione Berkeley DB. Ciò che questo fornisce allo sviluppatore di applicazioni SQLite è una libreria compatibile con SQLite con scalabilità, concorrenza e affidabilità (HA) aggiuntive, nonché altre funzionalità. Berkeley DB supporta più lettori e scritture che accedono contemporaneamente al database. Esistono due eccellenti white paper scritti da Mike Owens, autore di "The Definitive Guide to SQLite" che confronta Berkeley DB e SQLite (Performance comparison, Behavioral Differences).

Disclaimer: Sono uno dei product manager di Berkeley DB, quindi sono un po 'prevenuto. Ma richieste come la tua (hanno bisogno di maggiore concorrenza, scalabilità, affidabilità da SQLite) è esattamente il motivo per cui abbiamo deciso di fornire una libreria combinata che ti offre il meglio di entrambi i mondi.

+1

Solo una nota: i collegamenti completi (al contrario di quelli abbreviati) sono preferiti su SO in modo che possiamo vedere dove conducono. Hai anche un link per l'implementazione di Android? Saluti –

+0

Grazie per la risposta! Ma ho bisogno che la mia soluzione funzioni su dispositivi Android. Come ho capito, la tua soluzione non può funzionare su Android. (c'è una versione speciale incorporata di sqlite) –

+0

@Joseph, grazie per il suggerimento. Ho notato sia i collegamenti brevi che quelli completi utilizzati. Oggi puoi costruire BDB per Android e sostituire la libreria SQLite oppure collegare BDB all'applicazione e chiamarla localmente. C'è un buon post OTN su questo qui: http://forums.oracle.com/forums/thread.jspa?messageID=9440046� – dsegleau

2

Se si utilizza solo una classe di helper singleton per accedere al db non è necessario sincronizzarsi ed è possibile utilizzare l'helper da più lettori/scrittori poiché la classe helper gestisce la sincronizzazione stessa.

Cercare at this post per mor spiegazione dettagliata

+0

Quale versione di Android utilizzate? Perché ci sono stati problemi con questo nelle versioni precedenti. Non so quando Google ha risolto questo problema. Ma prima dovresti usare la tua sincronizzazione. Stava fallendo per me quando non lo stavo usando ed è stato corretto subito dopo aver aggiunto la sincronizzazione –

0

Utilizzare un aiutante Singleton per l'apertura di connessioni.

1) Aprire tutte le connessioni leggibili che si desidera e chiudere dopo aver terminato.

2) Per le connessioni scrivibili, è necessario aprire solo una singola connessione scrivibile.

Quando si tenta di aprire un'altra connessione scrivibile, restituire la connessione già aperta. Se non esiste alcuna connessione scrivibile, aprire una nuova connessione scrivibile. Mantieni un contatore writable_connection e chiudi la connessione scrivibile, quando tutti i thread sono finiti con esso.

Sto per implementarlo e farò sapere se funziona quando ho finito.

0

Bene, leggendo la documentazione la mia risposta precedente sembra errata. Ho un singolo (tonico) SQLiteOpenHelper, quindi sembra che tutte le connessioni siano uguali. Anche quelli leggibili sono uguali alle connessioni scrivibili.

Così ho intenzione di saltare chiamando getReadableDatabase e utilizzare solo getWritableDatabase. Quindi ho intenzione di tenere un contatore, per assicurarmi che il database venga chiuso una sola volta.

Poiché SQLiteOpenHelper serializza le scritture, tutto dovrebbe andare bene in questo modo. Dovrò semplicemente tenere traccia della connessione, per assicurarmi che non ne tenga uno aperto alla fine.

Quindi, nella mia classe DBHelper mi hanno ora:

private int activeDatabaseCount = 0; 
public synchronized SQLiteDatabase openDatabase() { 
    SQLiteDatabase connection = getWritableDatabase(); // always returns the same connection instance 
    activeDatabaseCount++; 
    return connection; 
} 
public synchronized void closeDatabase(SQLiteDatabase connection) { 
    activeDatabaseCount--; 
    if (activeDatabaseCount == 0) { 
     if (connection != null) { 
      if (connection.isOpen()) { 
       connection.close(); 
      } 
     } 
    } 
}