2011-09-22 29 views
8

Tengo un problema y no estoy seguro de cómo abordarlo. Una actividad en mi aplicación tiene múltiples AsyncTask que tienen acceso al número SQLiteOpenHelper. Inicializo y abrí el ayudante en onCreate() y lo estoy cerrando en onStop(). También compruebo si se ha inicializado en onResume().Android AsyncTask y SQLite DB instancia

Desde que publiqué mi aplicación, recibí varios errores con la excepción de nulo en doInBackground donde trato de acceder al asistente de DB. Sé que esto sucede porque el DB está cerrado (onStop()) justo antes de llamar al doInBackground, es suficiente.

Mi pregunta es, ¿dónde debo cerrar la conexión DB? ¿Es correcto usar una instancia única del ayudante DB en la Actividad y acceder a ella desde múltiples hilos (AsyncTasks)? ¿O debería usar una instancia auxiliar DB separada para cada AsyncTask?

Este es un esqueleto simplificada de mi actividad:

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

Respuesta

1

Mencionó que cancela la AsyncTask antes de cerrar la base de datos. Pero debe tener en cuenta que cancelar la AsyncTask simplemente indica que la tarea debe cancelarse y usted necesita verificar isCancelled() en doInBackground() y hacer lo necesario para detener las operaciones de DB.

Antes de cerrar la base de datos, debe verificar getStatus() para asegurarse de que la tarea AsyncTask se haya detenido.

+0

Entonces, básicamente, ¿debería usar while (! IsCancelled) {} ​​en doInBackground y realizar todos los cálculos dentro del ciclo? – Marqs

+0

Sí, y comprueba getStatus() en tu hilo principal para asegurarte de que AsyncTask ha finalizado. –

+0

OK, pero ¿qué sucede si lo controlo onStop() y no ha terminado? ¿Dónde cerraría la conexión DB entonces? – Marqs

5

Es bueno utilizar una sola DB Helper. El problema es que cuando el usuario abandona el Activity, el DB está cerrado, pero el AsyncTask aún se puede ejecutar. Por lo tanto, debe verificar que el DB no sea nulo cuando intente acceder a él, y si es null, esto puede significar que su Activity se destruyó y cancel esa tarea.

+0

Gracias por la respuesta rápida. Olvidé mencionar que también tengo algunas Excepciones que dicen "no se puede cerrar debido a declaraciones no finalizadas". Eso probablemente significa que trato de cerrar la conexión DB en onStop() mientras la consulta se ejecuta en doInBackground(). Cancelé la AsyncTask antes de cloro la base de datos, pero sigo recibiendo este error. ¿Alguna idea de por qué está sucediendo? – Marqs

+0

@Ovidiu ¿Dónde coloca generalmente el código de inicialización y limpieza de la base de datos? (Suponiendo que hay un DBHelper accedido por varias vistas). –

+0

como @boulder dijo que puede ser una buena práctica mantenerlo en la aplicación –

11

No necesita administrar la conexión db para cada actividad. Puede hacerlo en una instancia de android.app.Application y acceder a db utilizando esta instancia. Algo como esto:

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

De esta manera usted puede estar seguro de que DB es siempre accesible.

Y sí, tener una instancia de Db helper es una buena práctica. La sincronización de subprocesos está hecha para usted de manera predeterminada.

+0

+1 - esto es muy interesante, voy a analizar esto - ¡gracias! – Marqs

+0

Una pregunta más ... ¿dónde cerraría la conexión DB? – Marqs

+0

No lo cierro. Me pregunto si es realmente necesario. Si puede encontrar más detalles al respecto, comparta, es interesante. Pensé que nunca daría ningún problema con este enfoque. –

Cuestiones relacionadas