android - tiempo - cómo asegurarse de que solo se ejecute una AsyncTask en segundo plano
thread android studio (4)
Creo que el problema al que se enfrenta es que está iniciando AsyncTask de una actividad. Su actividad extiende ORMLiteBaseActivity, lo que abre el helper (y con eso la base de datos) onCreate y lo cierra onDestroy. Cuando sale de la actividad y la tarea en segundo plano aún no ha terminado, cuando intenta escribir en la base de datos termina con una base de datos cerrada.
ORMLite maneja las sincronizaciones internamente y nunca he necesitado hacer bloques sincronizados con ella. Lo uso en todos mis proyectos que requieren una base de datos.
También para las otras respuestas, el error es una base de datos cerrada y no operaciones de escritura simultáneas, por lo que la sincronización no tiene sentido.
Tengo una clase, DownloadAndSave
que se extiende desde AsyncTask
. En su método doInBackground
, recupera datos de una conexión http y guarda los datos usando OrmLite, pero también limpia las entradas antiguas de la base de datos. Entonces, algo como esto:
doInBackground()
{
clearDb();
dataList = fetchDataFromHttp();
saveToDb(dataList);
}
Frecuentemente recibo una excepción DB:
intentar reabrir un objeto ya cerrado: SQLiteDatabase
en las funciones clearDb () y saveToDb ().
Y esto es malo ya que los datos antiguos de la llamada anterior de DownloadAndSave
se mezclan con los nuevos datos de DownloadAndSave
.
En mi opinión, necesito asegurarme de que cuando empiece un hilo, todas las otras bandas de la clase DownloadAndSave
hayan terminado, o en otras palabras, necesito ejecutar como máximo una instancia de DownloadAndSave
a la vez. Entonces la pregunta es: ¿cómo me aseguro de que solo se ejecute una instancia de DownloadAndSave
en cualquier punto del tiempo?
Desde API versión 11 ( HONEYCOMB
) de la API de Android, se puede ejecutar una AsyncTask
en un Executor
determinado. Puede usar SerialExecutor por defecto para ejecutar tareas secuencialmente.
Si usa la operación db en doInBackground, debería estar bloqueado db para un hilo.
public void insertToDb(){
SQliteDatabase db;
...
db.beginTransaction();
...//operation
db.yieldIfContendedSafely();
db.setTransactionSuccessful();
db.endTransaction();
}
Opción 1 . Mover arriba:
clearDb();
dataList = fetchDataFromHttp();
saveToDb(dataList);
en una clase separada que se sincroniza con el objeto de la clase:
public class WorkerClass {
private WorkerListener workerListener;
public static interface WorkerListener {
public void publishWorkProgress(String data);
}
public WorkerClass(WorkerListener workerListener) {
this.workerListener = workerListener;
}
public void performWork() {
synchronized (WorkerClass.class) {
clearDb();
publish("Cleared DB");
dataList = fetchDataFromHttp();
publish("Got http data");
saveToDb(dataList);
publish("There! saved!");
}
}
private void publish(String message) {
if(workerListener != null) {
workerListener.publishWorkProgress(message);
}
}
}
Mientras desde tu actividad:
public class SampleActivity extends Activity {
public void doTheThing() {
new MyAsyncTask().execute();
}
private static class MyAsyncTask extends AsyncTask<Void, String, Void> implements WorkerListener {
@Override
protected Void doInBackground(Void... params) {
new WorkerClass(this).performWork();
return null;
}
@Override
public void publishWorkProgress(String data) {
publishProgress(data);
}
}
}
Opción 2 : mover el código anterior a un servicio de intendencia:
public class WorkerIntentService extends IntentService {
public WorkerIntentService() {
super(null);
}
@Override
protected void onHandleIntent(Intent intent) {
clearDb();
dataList = fetchDataFromHttp();
saveToDb(dataList);
}
}
El uso de un IntentService garantiza que las tareas se ejecuten en serie.