2012-11-27 235 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'值。 –