我有一个AsyncTask用于处理后台HTTP任务,它是按计划(Alarms/Service)运行的,有时用户也会手动执行它。
我从SQLite中处理记录,并注意到在服务器上出现了重复发布的情况,这告诉我有时预定任务和同时用户手动运行它导致相同的记录被读取并从数据库中处理两次。我在处理完成后删除记录,但仍然会出现这种情况。
我应该如何处理?也许需要组织某种类型的队列?
我有一个AsyncTask用于处理后台HTTP任务,它是按计划(Alarms/Service)运行的,有时用户也会手动执行它。
我从SQLite中处理记录,并注意到在服务器上出现了重复发布的情况,这告诉我有时预定任务和同时用户手动运行它导致相同的记录被读取并从数据库中处理两次。我在处理完成后删除记录,但仍然会出现这种情况。
我应该如何处理?也许需要组织某种类型的队列?
您可以使用executeOnExecutor()
在Executor
上执行您的AsyncTask。
为了确保线程以串行方式运行,请使用:SERIAL_EXECUTOR
。
其它: 如何使用Executor
如果多个活动访问您的DB,为什么不创建一种网关数据库助手,并使用synchronized
块确保只有一个线程可以同时访问它。
或者,您可以尝试使用以下方法来查看任务当前是否正在运行:
if (katitsAsyncTask.getStatus().equals(AsyncTask.Status.FINISHED))
katitsAsyncTask.execute();
else
// wait until it's done.
将AsyncTask初始化为null。只有在其为null时才创建一个新的AsyncTask。在onPostExecute中,最后将其再次设置为null。如果用户取消了该操作,在onCancelled中也要做同样的处理。下面是一些未经测试的代码,以说明基本思想。
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class FooActivity extends Activity {
private class MyAsyncTask extends AsyncTask<Foo, Foo, Foo> {
@Override
protected void onPostExecute(Foo foo) {
// do stuff
mMyAsyncTask = null;
}
@Override
protected void onCancelled() {
// TODO Auto-generated method stub
mMyAsyncTask = null;
}
@Override
protected Foo doInBackground(Foo... params) {
try {
// dangerous stuff
} catch (Exception e) {
// handle. Now we know we'll hit onPostExecute()
}
return null;
}
}
private MyAsyncTask mMyAsyncTask = null;
@Override
public void onCreate(Bundle bundle) {
Button button = (Button) findViewById(R.id.b2);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (mMyAsyncTask == null) {
mMyAsyncTask = new MyAsyncTask();
mMyAsyncTask.execute(null);
}
}
});
}
}
我知道这已经过去一段时间了,而且你已经解决了你的问题。但是我刚刚遇到了类似的问题。Reno的建议让我找到了正确的方向,但是对于那些发现填补空白很困难的人来说,这就是我如何克服与katit类似问题的方法。
我希望特定的AsyncTask只在没有运行时才运行。正如Reno的建议所述,AsyncTask接口已被创建以处理所有繁琐的进程,以便正确地处理Android线程。这意味着,执行程序已内置。正如blog所建议的:
“当在AsyncTask上调用execute(Object.. params)时,任务将在后台线程中执行。根据平台的不同,AsyncTasks可以串行执行(1.6之前和4+可能再次执行),也可以并行执行(1.6-3.2)。
为确保按您要求串行或并行运行,从API Level 11开始,您可以使用executeOnExecutor(Executor executor, Object.. params)方法,并提供一个执行程序。平台为方便起见提供了两个执行程序,分别为AsyncTask.SERIAL_EXECUTOR和AsyncTask.THREAD_POOL_EXECUTOR。”
因此,你可以通过AsyncTask接口来进行线程阻塞,这也意味着你可以像DeeV在post中建议的那样,简单地使用AsyncTasks.getStatus() 来处理线程阻塞。
在我的代码中,我通过以下方式处理:
创建一个全局变量:
private static AsyncTask<String, Integer, String> mTask = null;
在onCreate中,将其初始化为我的AsyncTask的实例,称为CalculateSpecAndDraw:
mTask = new CalculateAndDrawSpec();
if(mTask.getStatus() == AsyncTask.Status.FINISHED){
// My AsyncTask is done and onPostExecute was called
mTask = new CalculateAndDrawSpec().execute(Integer.toString(progress));
}else if(mTask.getStatus() == AsyncTask.Status.PENDING){
mTask.execute(Integer.toString(progress));
}else{
Toast.makeText(PlaySpecActivity.this, "Please Wait..", 1000).show();
}
如果一个线程已经完成,此操作将会生成一个新的线程;或者如果线程状态为PENDING即已定义但未启动,则我们启动它。但如果线程正在运行,我们不会重新运行它,只是简单地通知用户它尚未完成,或执行任何我们希望进行的操作。如果您想安排下一个事件而不仅仅是阻止其重新运行,请参阅this关于使用执行器的文档。
你可以尝试将你的 check-what-to-send 和 send-it 逻辑放在一个 synchronized
方法中,这种方法对我们来说似乎很有效。
尝试创建一些实例布尔值,在异步任务的preexecute中将其设置为“true”,然后在postexecute中将其设置为“false”。然后,可能在doinbackground中检查该布尔值是否为true。如果是,则调用特定重复任务的cancel方法。