2012-11-27 236 views
5

我在点击一个按钮时从我的UI线程调用一个asynctask,该按钮执行一些HTTP-Get请求。防止多次执行asynctask

如果用户在当前任务完成之前再次单击该按钮,会发生什么情况?这是由asynctask内部处理,还是我需要自己照顾它?

+1

的AsyncTask的实例可以永远不会被执行不止一次。 – njzk2

+0

@ njzk2尽管每个按钮点击都会创建一个新实例。 – Johan

+1

那么它取决于你。我建议在单击时停用按钮,并且仅在完成asynctask时重新激活它(最有可能在onPostExecute中)。你也可以只有一个asynctask实例。 – njzk2

回答

6

有两种方法可以做到这一点。

1:显示进度对话框并阻止用户进一步输入。

2:在类范围private中创建AsyncTask变量。那么这将永远不会运行多次。

+0

我明白了。关于你的第二个选择:我应该在我的'onCreate'方法上设置asynctask,并且在我的按钮中执行'execute()'? – Johan

+0

@Johan如果你这样做,你将无法再次运行它。有一个asynctask成员,所以你可以检查它的状态,如果它完成了,你可以创建一个新的asynctask并分配给同一个成员。 – lenik

+0

是的,你可以做到这一点。并检查已经运行的异常。 – Naeem

2

您可以拨打来检查它是否已在运行并忽略按钮点击。

要做到这一点,你绝对需要将AsyncTask的实例保存到某个地方并且可以访问它。

+0

好的,但如果它已经运行并且我再次按下它会发生什么?它似乎没有抛出任何的豁免。 – Johan

+0

你可以为每个按钮或类似的东西保留一个asynctask,如果它已经运行,只需忽略按钮点击。 – lenik

1

将产生另一个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; 
    ... 
1

两种方式:

  1. 在的AsyncTask的onPreExeccute()显示ProgressDialog其setCancelable(false)。 AsyncTask的在ProgressDialog上调用dismiss()。这将显示阻止进度对话框。您也可以在此处显示一些消息或进度。

  2. 在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(); 
} 
+0

进度对话很糟糕...你有没有在google +或gmail应用程序中看到进度对话框? ...更好的模式是在某处放置未确定的未确定进度并禁用不需要的UI(如此按钮),所以-1代表对话框,+1代表禁用按钮 – Selvin

+0

@Selvin选项是一个选项:)。另外,Google创建了'ProgressDialog',因此至少在某些情况下最好有一些用处。 –

+0

@Selvin就像您的手机关机时一样,他们无法冒险让您对UI执行其他任何操作。 –

4

为什么不使用布尔标志,并检查它的状态呢?我在永无止境的名单中使用这个逻辑,并且从未失败过。

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

如果你曾经取消一个'AsyncTask'(通过调用'cancel'),那么你还需要切换'onCancelled'中的标志,因为'onPostExecute'不会被调用。 –

+0

@ dave.c:的确如此。但在这个问题的背景下,我觉得答案就足够了。 –

+0

我不认为这会起作用。由于您正在调用新的'AsyncTask',因此您在不同的线程中更改'loadingData'值。 –