如何确保只有一个AsyncTask在后台运行

时间:2013-10-08 11:51:32

标签: android sqlite android-asynctask ormlite

我有一个类DownloadAndSave,它来自AsyncTask。在其doInBackground方法中,它从http连接检索数据并使用OrmLite保存数据,但也清除数据库中的旧条目。所以,像这样:

doInBackground()
{
  clearDb();
  dataList = fetchDataFromHttp();
  saveToDb(dataList);
}

我经常遇到数据库异常:

  

尝试重新打开已关闭的对象:SQLiteDatabase

在clearDb()和saveToDb()函数中。

这很糟糕,因为之前调用DownloadAndSave的旧数据与来自DownloadAndSave的新数据混合在一起。

在我看来,我需要确保当我开始一个线程时,来自DownloadAndSave类的所有其他步骤都已完成,或者换句话说,我需要运行最多一个{{1}的实例1}}一次。所以问题是:我如何确保只有DownloadAndSave的一个实例可以在任何时间点运行?

4 个答案:

答案 0 :(得分:3)

选项1 。移到上面:

clearDb();
dataList = fetchDataFromHttp();
saveToDb(dataList);

在一个与类对象同步的单独类中:

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

}

来自您的活动:

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

选项2 :将以上代码移至IntentService:

public class WorkerIntentService extends IntentService {
   public WorkerIntentService() {
       super(null);
   }

   @Override
    protected void onHandleIntent(Intent intent) {
       clearDb();
       dataList = fetchDataFromHttp();
       saveToDb(dataList);
    }
}

使用IntentService可以保证任务以串行方式执行。

答案 1 :(得分:2)

自Android API的API版本11(HONEYCOMB)起,AsyncTask在给定executed上可为Executor。您可以使用默认的SerialExecutor按顺序执行任务。

答案 2 :(得分:0)

如果在doInBackground中使用db操作,则应该为一个线程锁定db。

public void insertToDb(){
 SQliteDatabase db;
 ...
 db.beginTransaction();
 ...//operation
 db.yieldIfContendedSafely();
 db.setTransactionSuccessful();
 db.endTransaction();
}

答案 3 :(得分:0)

我相信您遇到的问题是您正在从活动中启动AsyncTask。您的活动是扩展ORMLiteBaseActivity,它打开帮助程序(以及数据库)onCreate并将其关闭onDestroy。当您退出活动并且后台任务仍然没有完成时,那么当您尝试写入数据库时​​,您最终会关闭一个数据库。

ORMLite在内部处理同步,我从来不需要用它来执行同步块。我在需要数据库的所有项目中使用它。

另外,对于其他答案,错误是一个封闭的数据库而不是并发写操作,因此同步没有意义。