2011-09-22 15 views
8

Ho un problema e non sono sicuro di come affrontarlo. Un'attività nella mia app ha più AsyncTask s che accedono al singolo SQLiteOpenHelper. Inizializzo e apro l'helper in onCreate() e lo chiudo in onStop(). Controllo anche se è stato inizializzato in onResume().Istanza Android AsyncTask e SQLite DB

Da quando ho pubblicato la mia app ho ricevuto il numero di errori con Null Exception in doInBackground dove cerco di accedere all'helper DB. So che questo succede perché il DB è chiuso (onStop()) poco prima che venga chiamato il numero doInBackground, abbastanza equo.

La mia domanda è, dove devo chiudere la connessione DB? È corretto utilizzare una singola istanza dell'helper DB nell'attività e accedervi da più thread (AsyncTasks)? O dovrei usare un'istanza helper DB separata per ogni AsyncTask?

Si tratta di uno scheletro semplificata della mia attività:

public class MyActivity extends Activity{ 
    private DbHelper mDbHelper; 
    private ArrayList<ExampleObject> objects; 

    @Override 
    public void onStop(){ 
     super.onStop(); 
     if(mDbHelper != null){ 
      mDbHelper.close(); 
      mDbHelper = null; 
     } 
    } 

    @Override 
    public void onResume(){ 
     super.onResume(); 
     if(mDbHelper == null){ 
      mDbHelper = new DbHelper(this); 
      mDbHelper.open(); 
     } 
    } 

    @Override 
    public void onCreate(Bundle icicle) { 
     super.onCreate(icicle); 
     DbHelper mDbHelper = new DbHelper(this); 
     mDbHelper.open(); 
    } 

    private class DoSomething extends AsyncTask<String, Void, Void> { 

     @Override 
     protected Void doInBackground(String... arg0) { 
      objects = mDbHelper.getMyExampleObjects(); 
      return null; 
     } 

     @Override 
     protected void onPostExecute(final Void unused){ 
      //update UI with my objects 
     } 
    } 

    private class DoSomethingElse extends AsyncTask<String, Void, Void> { 

     @Override 
     protected Void doInBackground(String... arg0) { 
      objects = mDbHelper.getSortedObjects(); 
      return null; 
     } 

     @Override 
     protected void onPostExecute(final Void unused){ 
      //update UI with my objects 
     } 
    } 
} 

risposta

1

Hai menzionato che annulli AsyncTask prima di chiudere il DB. Ma dovresti tenere a mente che l'annullamento di AsyncTask segnala solo l'attività da annullare e devi controllare isCancelled() in doInBackground() e fare il necessario per interrompere le operazioni di DB.

Prima di chiudere il DB, è necessario controllare getStatus() per assicurarsi che il AsyncTask sia stato arrestato.

+0

Quindi in pratica dovrei usare mentre (! IsCancelled) {} ​​in doInBackground ed eseguire tutti i calcoli all'interno del ciclo? – Marqs

+0

Sì, e controlla getStatus() nel thread principale per essere sicuro che il AsyncTask sia terminato. –

+0

OK ma cosa succede se lo controllo in onStop() e non è finito? Dove dovrei chiudere la connessione DB allora? – Marqs

5

E 'bene utilizzare un unico DB Helper. Il problema è che quando l'utente lascia lo Activity il DB è chiuso, ma il AsyncTask può ancora essere eseguito. Quindi dovresti controllare che lo DB non sia nullo quando stai provando ad accedervi, e se è null ciò potrebbe significare che il tuo Activity è stato distrutto e che l'attività è cancel.

+0

Grazie per la risposta veloce. Ho dimenticato di dire che ho anche ricevuto alcune eccezioni che dicevano "impossibile chiudere a causa di dichiarazioni non finalizzate". Questo probabilmente significa che cerco di chiudere la connessione DB in onStop() mentre la query è in esecuzione in doInBackground(). Annullo il AsyncTask prima di clsoing il DB ma ottengo ancora questo errore. Qualche idea sul perché sta succedendo? – Marqs

+0

@Ovidiu In quale posizione si colloca in genere il codice di inizializzazione e di pulitura del DB? (Supponiamo che ci sia un DBHelper acceduto da più viste). –

+0

come @boulder ha detto che potrebbe essere una buona pratica tenerlo in applicazione –

11

Non è necessario gestire la connessione db per ogni attività. Puoi farlo in un'istanza di android.app.Application e accedere a db utilizzando questa istanza. Qualcosa di simile a questo:

public class MyApplication extends Application { 

    // Synchronized because it's possible to get a race condition here 
    // if db is accessed from different threads. This synchronization can be optimized 
    // thought I wander if it's necessary 
    public synchronized static SQLiteDatabase db() { 
     if(self().mDbOpenHelper == null) { 
      self().mDbOpenHelper = new MyDbOpenHelper(); 
     } 
     return self().mDbOpenHelper.getWritableDatabase(); 
    } 

    public static Context context() { 
     return self(); 
    } 

    @Override 
    public void onCreate() { 
     super.onCreate(); 
     mSelf = this; 
    } 

    private static MyApplication self() { 
     if (self == null) throw new IllegalStateException(); 
     return mSelf; 
    } 

    private MyDbOpenHelper mDbOpenHelper; 

    private static MyApplication mSelf; 
} 

In questo modo si può essere sicuri di DB è sempre accessibile.

E sì, avere un'istanza di Db helper è una buona pratica. La sincronizzazione dei thread è fatta per te per impostazione predefinita.

+0

+1 - questo è molto interessante, guarderò in questo - grazie! – Marqs

+0

Un'altra domanda ... dove chiudere la connessione DB? – Marqs

+0

Non lo chiudo. Mi chiedo se è davvero necessario. Se riesci a trovare maggiori dettagli su questo per favore condividi, è interessante. Ho pensato di non passare mai nessun problema con questo approccio. –