我在点击一个按钮时从我的UI线程调用一个asynctask,该按钮执行一些HTTP-Get请求。防止多次执行asynctask
如果用户在当前任务完成之前再次单击该按钮,会发生什么情况?这是由asynctask内部处理,还是我需要自己照顾它?
我在点击一个按钮时从我的UI线程调用一个asynctask,该按钮执行一些HTTP-Get请求。防止多次执行asynctask
如果用户在当前任务完成之前再次单击该按钮,会发生什么情况?这是由asynctask内部处理,还是我需要自己照顾它?
将产生另一个AsyncTask,这可能会导致您的困难 - 如果您持续保存数据,则会保存数据的竞争条件等。我建议在接收onClick
事件时禁用该按钮并显示一些说明的微调背景中发生了一些事情。
我们已经实现了与放置在操作栏中的刷新按钮类似的功能。请注意,我们的异步任务在app.Service
下运行,因此在配置更改期间(其中hasStartedSync
获得重新初始化),我们依赖“isTheSyncServiceRunning”检查。
// on setting the selection buttons up
public void onPrepareOptionsMenu(final Menu menu) {
super.onPrepareOptionsMenu(menu);
final MenuItem refreshItem = menu.findItem(id.sync);
if (isSynchronisationServiceRunning() || hasStartedSync) {
refreshItem.setEnabled(false);
if (Build.VERSION.SDK_INT > 10) {
// Replace the refresh icon with an indeterminate spinner for > 10 API.
final LayoutInflater inflater = (LayoutInflater)getSherlockActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
refreshItem.setActionView(inflater.inflate(layout.actionbar_indeterminate_progress, null));
}
} else {
refreshItem.setEnabled(true);
refreshItem.setActionView(null);
}
}
// on selection.
public boolean onOptionsItemSelected(final MenuItem item) {
boolean isSelected;
switch (item.getItemId()) {
case id.sync:
// Fire request to start GET here.
hasStartedSync = true;
invalidateOptionsMenu();
break;
...
两种方式:
在的AsyncTask的onPreExeccute()
显示ProgressDialog其setCancelable(false)
。 AsyncTask的在ProgressDialog上调用dismiss()
。这将显示阻止进度对话框。您也可以在此处显示一些消息或进度。
在AsyncTask的onPreExeccute()
中,在Button上调用setEnabled(false)
。 AsyncTask的在Button上调用setEnabled(true)
。只要任务正在运行,这将禁用按钮。您也可以在这些位置更改按钮文字。甚至暂时用进度微调代替它(如在ActionBar中)。
此外,您可以在“运行”和“取消”行为之间切换按钮,就像现代Web浏览器中的刷新按钮一样。这将给用户更多的控制权。
一个值得注意的:在try-catch
包裹内onDoInBackground()
一切,这样的AsyncTask始终正常退出,并onPostExecute()
总是被调用。你可以根据onPostExecute(result)
的结果参数判断HTTP请求是否有任何东西。
例子:只需调用此方法来从服务器加载一些文本到TextView
上的一些Button
点击:
public static void runHTTP(final TextView targetView, final Button triggerBtn){
//---new task----
AsyncTask<Void,Void,String> task = new AsyncTask<Void, Void, String>(){
@Override
protected void onPreExecute() {
super.onPreExecute();
//----disable button---
triggerBtn.setEnabled(false);
//---show message----
targetView.setText("Loading...");
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
//----update result---
targetView.setText(result);
//----re-enable button---
triggerBtn.setEnabled(true);
}
@Override
protected String doInBackground(Void... voids) {
//---default value--
String result = "No Data";
//--for safety--
try{
//-----do time consuming stuff here ---
}catch (Exception e){
//---error value--
result = "Error fetching data";
}
return result;
}
};
//----run task----
task.execute();
}
进度对话很糟糕...你有没有在google +或gmail应用程序中看到进度对话框? ...更好的模式是在某处放置未确定的未确定进度并禁用不需要的UI(如此按钮),所以-1代表对话框,+1代表禁用按钮 – Selvin
@Selvin选项是一个选项:)。另外,Google创建了'ProgressDialog',因此至少在某些情况下最好有一些用处。 –
@Selvin就像您的手机关机时一样,他们无法冒险让您对UI执行其他任何操作。 –
为什么不使用布尔标志,并检查它的状态呢?我在永无止境的名单中使用这个逻辑,并且从未失败过。
在onPostExecute()
@Override
protected void onPostExecute(Void result) {
// CHANGE THE LOADINGMORE STATUS TO PERMIT FETCHING MORE DATA
loadingData = false;
}
而且按钮的点击收听
切换在doInBackground()
@Override
protected Void doInBackground(Void... params) {
loadingData = true;
}
的标志,然后再
your_btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (loadingData == false) {
// RUN THE ASYNCTASK AGAIN
} else if (loadingData == true) {
// SHOW A TOAST THAT YOU ARE ALREADY FETCHING DATA
}
}
});
如果你曾经取消一个'AsyncTask'(通过调用'cancel'),那么你还需要切换'onCancelled'中的标志,因为'onPostExecute'不会被调用。 –
@ dave.c:的确如此。但在这个问题的背景下,我觉得答案就足够了。 –
我不认为这会起作用。由于您正在调用新的'AsyncTask',因此您在不同的线程中更改'loadingData'值。 –
的AsyncTask的实例可以永远不会被执行不止一次。 – njzk2
@ njzk2尽管每个按钮点击都会创建一个新实例。 – Johan
那么它取决于你。我建议在单击时停用按钮,并且仅在完成asynctask时重新激活它(最有可能在onPostExecute中)。你也可以只有一个asynctask实例。 – njzk2