Autore Topic: SQLite e Multithreading  (Letto 2511 volte)

Offline vavabigol

  • Nuovo arrivato
  • *
  • Post: 34
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    HTC Desire - CM 7.2.0.1
  • Sistema operativo:
    Ubuntu 11.10 - Windows 7
SQLite e Multithreading
« il: 21 Febbraio 2012, 16:54:57 CET »
0
Ciao a tutti.. ho un problema che non riesco a risolvere. Spero che voi mi possiate aiutare ;)

Nella mia app ho un Servizio che ogni x minuti esegue dei controlli ed in base all'esito svolge determinate azioni;
Queste azioni possono prevedere l'aggiornamento di alcuni dati all'interno del mio DB SQLite.
Il lavoro viene svolto da un thread separato.

Nel frattempo nella mia MainActivity io posso lavorare senza accorgermi del lavoro che viene svolto in background. Seguendo consigli trovati in rete, dovendo spesso utilizzare il db nel corso dell'utilizzo dell'app, apro la connessione nel metodo onResume() e la chiudo nell' onPause()

Il problema è proprio questo.. nel momento in cui blocco e sblocco il mio telefono, se in background sto eseguendo operazioni l'app va in errore dicendomi che il database è bloccato.. e io non so come fare per evitare questo errore..! Ho capito che l'errore proviene dal metodo getWritableDatabase() della mia classe che estende SQLiteOpenHelper... ma come posso evitarlo?

Questa è la parte di LogCat che spiega l'errore

02-21 16:51:58.740: E/AndroidRuntime(31299): Caused by: android.database.sqlite.SQLiteDatabaseLockedException: database is locked
02-21 16:51:58.740: E/AndroidRuntime(31299):    at android.database.sqlite.SQLiteDatabase.dbopen(Native Method)
02-21 16:51:58.740: E/AndroidRuntime(31299):    at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:983)
02-21 16:51:58.740: E/AndroidRuntime(31299):    at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:956)
02-21 16:51:58.740: E/AndroidRuntime(31299):    at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:1021)
02-21 16:51:58.740: E/AndroidRuntime(31299):    at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:772)
02-21 16:51:58.740: E/AndroidRuntime(31299):    at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:221)
02-21 16:51:58.740: E/AndroidRuntime(31299):    at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:149)

Offline Brig

  • Nuovo arrivato
  • *
  • Post: 17
  • Respect: +1
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S
  • Sistema operativo:
    Ubuntu 10.4, Windows 7
Re:SQLite e Multithreading
« Risposta #1 il: 21 Febbraio 2012, 21:31:25 CET »
0
potresti considerare un content provider, se può farti comodo avere a disposizione i dati anche per altre applicazioni...

altrimenti... mi verrebbe da suggerirti la sincronizzazione sul db... se è in uso non chiudi, altrimenti aspetti la fine dell'utilizzo del db e poi lo chiudi..

Offline vavabigol

  • Nuovo arrivato
  • *
  • Post: 34
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    HTC Desire - CM 7.2.0.1
  • Sistema operativo:
    Ubuntu 11.10 - Windows 7
Re:SQLite e Multithreading
« Risposta #2 il: 22 Febbraio 2012, 08:43:59 CET »
0
il content provider purtroppo non mi è utile.. i dati non devono essere a disposizione di altre applicazioni...
Cosa intendi per sincronizzazione del db? Il punto è che credevo fosse possibile ottenere in scrittura l'accesso contemporaneo al db.. cosa che evidentemente non è gestita ( o io non so come si fa almeno :( ).

Il punto è : se ho un thread in background che esegue operazioni sul db non posso scrivere nello stesso db dal thread UI?
Nella SQLiteDatabase ho visto che c'è il metodo 'enableWriteAheadLogging' .. ma non sembra cambiare nulla...

Offline vavabigol

  • Nuovo arrivato
  • *
  • Post: 34
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    HTC Desire - CM 7.2.0.1
  • Sistema operativo:
    Ubuntu 11.10 - Windows 7
Re:SQLite e Multithreading
« Risposta #3 il: 22 Febbraio 2012, 09:09:22 CET »
0
Ora dico la cazzata ma... se estendessi la classe Application e mettessi l'apertura e la chiusura della mia classe Database rispettivamente nella onCreate e onTerminate ?

Poi sfrutto enableWriteAheadLogging() e synchronized per la concorrenza.. e metto un metodo per ottenere l'istanza della classe in modo da ottenerla dove mi serve...

E' una soluzione che fa un po' schifo ma non mi viene un altro modo..

Offline Brig

  • Nuovo arrivato
  • *
  • Post: 17
  • Respect: +1
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S
  • Sistema operativo:
    Ubuntu 10.4, Windows 7
Re:SQLite e Multithreading
« Risposta #4 il: 22 Febbraio 2012, 10:55:01 CET »
0
non ho idea se possa funzionare..

prova a mettere un blocco synchronized sul db
Codice (Java): [Seleziona]
synchronized(db){...} ovunque tocchi il db... l'unica cosa che db non deve mai essere null... neanche all'inizio...

Offline Ricky`

  • Amministratore
  • Utente storico
  • *****
  • Post: 3487
  • Respect: +506
    • Github
    • Google+
    • rciovati
    • Mostra profilo
Re:SQLite e Multithreading
« Risposta #5 il: 22 Febbraio 2012, 11:19:15 CET »
0
Secondo me bisognerebbe capire sto database perchè rimane bloccato.
Non viene chiuso?

Io comunque mi accodo al suggerimento di utilizzare il locking.
Puoi fare come ti ha suggerito Brig oppure, ancora in modo più semplice definire il metodo synchronized:

Codice (Java): [Seleziona]
public synchronized void performOperation(){

        myDb = getWritableDatabase();

        Log.d("performOperation", "start");

        //fai le operazioni

        myDb.close();

        Log.d("performOperation", "end");

}

Offline vavabigol

  • Nuovo arrivato
  • *
  • Post: 34
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    HTC Desire - CM 7.2.0.1
  • Sistema operativo:
    Ubuntu 11.10 - Windows 7
Re:SQLite e Multithreading
« Risposta #6 il: 22 Febbraio 2012, 12:16:01 CET »
0
Allora, forse spiegando meglio riesco a far capire il problema ;) scusatemi.

Il thread in background ha il compito di allineare le tabelle del db in locale con quelle di un sql server remoto.. effettua controlli a scadenza di tempo fisso e in caso trovi differenze esegue le query necessarie. Il tutto ovviamente senza mostrare nulla all'utente.
Per fare questo, nel servizio che esegue il lavoro, istanzio una classe 'Database' che è quella che possiede i metodi open(), close() e tutti i metodi necessari alle operazioni da eseguire.
Nel servizio l'apertura della connessione avviene nella onCreate, e la chiusura dell'onDestroy(). In sostanza rimane sempre aperta finché il servizio non muore. Dato che i controlli avvengono piuttosto spesso (< 1 minuto) mi sembrava uno spreco continuare ad aprire e chiudere la connessione.. ( cazzata?)

Nel frattempo nelle varie activity il lavoro svolto dall'utente può andare a scrivere dati nel db locale. Per fare questo istanzio un'altra classe Database, eseguendo open() nell'onResume() e il close() della connessione nell'onPause();

Secondo me il casino avviene quando, spegnendosi lo schermo per i tempi d'attesa, l'activity va in pausa ( chiudendo quindi la sua connessione col db); risvegliandosi , eseguendo l'onResume(), il tentativo di aprire la connessione con il database fallisce quando il thread in background sta eseguendo qualche operazione, perche il database è già bloccato da lui in scrittura.

Ovviamente il problema potrebbe presentarsi in qualsiasi momento suppongo; ho strutturato l'app in questo modo perchè pensavo che fosse possibile scrivere contemporaneamente sul db. Android/SQLite non si smazzano la concorrenza in scrittura?

Il punto è: anche mettendo il synchronized, getWritableDatabase() va in errore se un altro thread sta eseguendo nello stesso istante operazioni in scrittura sul db.. dicendomi che è bloccato..

Spero di aver chiarito la mia situazione!

Offline Ricky`

  • Amministratore
  • Utente storico
  • *****
  • Post: 3487
  • Respect: +506
    • Github
    • Google+
    • rciovati
    • Mostra profilo
Re:SQLite e Multithreading
« Risposta #7 il: 22 Febbraio 2012, 12:20:18 CET »
0
Citazione
Nel servizio l'apertura della connessione avviene nella onCreate, e la chiusura dell'onDestroy(). In sostanza rimane sempre aperta finché il servizio non muore. Dato che i controlli avvengono piuttosto spesso (< 1 minuto) mi sembrava uno spreco continuare ad aprire e chiudere la connessione.. ( cazzata?)

Eh, non è che forse ti conviene aprirlo prima di usarlo e chiuderlo subito dopo averlo usato?

Offline vavabigol

  • Nuovo arrivato
  • *
  • Post: 34
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    HTC Desire - CM 7.2.0.1
  • Sistema operativo:
    Ubuntu 11.10 - Windows 7
Re:SQLite e Multithreading
« Risposta #8 il: 22 Febbraio 2012, 12:40:49 CET »
0
si, quello di sicuro. Ma teoricamente il problema si pone ugualmente, no?

Nella situazione in cui l'utente ha fatto qualcosa per cui io devo scrivere nel db qualche dato.. se durante la scrittura di tale dato, effettuata supponiamo dal metodo scrivi1(), il thread si risveglia e tenta di aprire la connessione per scrivere qualcosa tramite il metodo scrivi2() vado comunque in eccezione... perché per aprire la connessione devo richiedere l'accesso tramite getWritableDatabase() che mi causa database locked.. sbaglio?

Offline emacav

  • Nuovo arrivato
  • *
  • Post: 6
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    LG P500
  • Sistema operativo:
    Windows7, Ubuntu 11.10
Re:SQLite e Multithreading
« Risposta #9 il: 22 Febbraio 2012, 13:49:56 CET »
0
Scusate, ma da una semplice ricerca su google si trova che per risolvere problemi di lock il trucco è di utilizzare una singola classe SQLiteOpenHelper e quindi una sola connessione verso il db, in questo modo la concorrenza viene gestita automaticamente dal sistema.
Puoi creare un Singleton per aprire il db una sola volta nell'applicazione

Offline vavabigol

  • Nuovo arrivato
  • *
  • Post: 34
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    HTC Desire - CM 7.2.0.1
  • Sistema operativo:
    Ubuntu 11.10 - Windows 7
Re:SQLite e Multithreading
« Risposta #10 il: 22 Febbraio 2012, 14:19:02 CET »
0
esatto, è quello che ho trovato anche io.
Le due soluzioni che ho trovato sono state :
1 - Variabile nell'Application
2 - Creare singleton per SQLiteOpenHelper

quale scegliere? Io ora mi sono fatto la prima.. voi consigliate la seconda?

Offline emacav

  • Nuovo arrivato
  • *
  • Post: 6
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    LG P500
  • Sistema operativo:
    Windows7, Ubuntu 11.10
Re:SQLite e Multithreading
« Risposta #11 il: 22 Febbraio 2012, 14:48:54 CET »
0
Io opterei per la seconda strada..
Prova a dare un'occhiata a questo http://kagii.com/post/6828016869/android-sqlite-locking

Offline Brig

  • Nuovo arrivato
  • *
  • Post: 17
  • Respect: +1
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S
  • Sistema operativo:
    Ubuntu 10.4, Windows 7
Re:SQLite e Multithreading
« Risposta #12 il: 22 Febbraio 2012, 19:58:06 CET »
0
Il punto è: anche mettendo il synchronized, getWritableDatabase() va in errore se un altro thread sta eseguendo nello stesso istante operazioni in scrittura sul db.. dicendomi che è bloccato..

mi fai vedere il codice? mi sembra strano... se hai sincronizzato a modo avviene la mutua esclusione...

cmq in parole povere vuoi "tenere aggiornato" il DB_2 che deve rimanere uguale al DB_1?

allora io ti direi di usare il content provider... non ti serve aggiornare a mano e risolvi una marea di problemi  ;-)

Offline vavabigol

  • Nuovo arrivato
  • *
  • Post: 34
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    HTC Desire - CM 7.2.0.1
  • Sistema operativo:
    Ubuntu 11.10 - Windows 7
Re:SQLite e Multithreading
« Risposta #13 il: 23 Febbraio 2012, 08:52:50 CET »
0
no non ho scritto codice, era un pensiero sul synchronized! Il problema secondo me è che nella open() e nella close() della mia classe database istanzio sempre una classe SQLiteOpenHelper.. ed è un errore.. no? Cambiato quello effettivamente il problema è sparito! ;)

Sono più incuriosito invece da quello che hai detto sul content provider per sincronizzare i DB.. devo aprire un altro topic? Tu come faresti per sincronizzare i DB con i content provider? Grazie  ;-)

Offline undead

  • Utente senior
  • ****
  • Post: 666
  • Respect: +113
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S6
  • Play Store ID:
    DrKappa
  • Sistema operativo:
    Windows 10 64-bit, Windows 8.1 64-bit
Re:SQLite e Multithreading
« Risposta #14 il: 23 Febbraio 2012, 09:12:12 CET »
0
Scusa se mi permetto ma le eccezioni servono proprio in casi come questo. Tu vuoi fare una operazione, il database è bloccato, ti intercetti la databaselockedexception, riprovi fino a quando non si sblocca e nel tuo servizio apri/chiudi la connessione solo quando ti serve davvero.

 :-o