2013-06-26 107 views
13

這可能是一個重複的問題,但我沒有找到我要找的東西。 我在UI活動new LoadData().execute();和doInBackground中調用一個AsyncTask,我調用一個需要時間的方法。如果數據在一段時間後沒有返回,我想中斷這個線程。 下面是我試圖做到這一點的代碼。一段時間後取消AsyncTask

class LoadData extends AsyncTask<String, String, String> 
{ 
    @Override 
    protected void onPreExecute() { 
    super.onPreExecute(); 
    startTime = System.currentTimeMillis(); 
    } 
    protected String doInBackground(String... args) 
    { 

     DataCollector dc = new DataCollector(); 
     data = dc.collectData(query); 
     //Here I check if the time is greater than 30 seconds then cancel 
     if(((System.currentTimeMillis()-startTime)/1000)>30) 
     { 
      cancel(true); 
     } 
    return null; 
    } 
} 

但是這並不能阻止30秒後的任務,實際上它需要更多的時間。 我也試過get(long timeout, TimeUnit unit);,但那也行不通。

任何人都可以告訴我我該怎麼做,或者如何在doInBackground中使用isCancelled()。

謝謝。

+0

也許這個答案可以幫助你:http://stackoverflow.com/a/11191070/3307066。 – jbarrameda

回答

10

您需要一個線程,在一段時間後取消您的任務。該線程可能看起來像這樣:

public class TaskCanceler implements Runnable{ 
    private AsyncTask task; 

    public TaskCanceler(AsyncTask task) { 
     this.task = task; 
    } 

    @Override 
    public void run() { 
     if (task.getStatus() == AsyncTask.Status.RUNNING) 
      task.cancel(true); 
    } 
} 

當你打電話給你的AsyncTask,需要一定的時間後運行CANCLE任務(=超時,在這種情況下,20秒)

private Handler handler = new Handler(); 
private TaskCanceler taskCanceler; 
... 
LoadData task = new LoadData(); 
taskCanceler = new TaskCanceler(task); 
handler.postDelayed(taskCanceler, 20*1000); 
task.execute(...) 

如果你打掃一下就取消或

if(taskCanceler != null && handler != null) { 
    handler.removeCallbacks(taskCanceler); 
} 

當然,你可以把這個包中的AsyncTask的自定義實現做完這是一個好主意。我多次使用這種模式,它的作用就像一個魅力。有一點需要注意,在罕見的情況下,處理程序不會啓動,我懷疑如果你在錯誤的上下文中創建它,在某些情況下它將不會存活,所以我強制處理程序成爲UI線程,handler= new Handler(Looper.getMainLooper());

+0

謝謝....我會嘗試這個,並會讓你知道... –

3

您必須對其他線程進行時間檢查。

你目前所做的是:執行dc.collectData(query)(在後臺),一旦準備就緒,你檢查你是否應該取消。因此,如果查詢需要1分鐘,您將在1分鐘後執行取消檢查,這已經太晚了。

你可以做的是安排應該在LoadData後運行30秒()一個TimerTask。執行(),如果定時器任務運行時,你可以取消的AsyncTask(如果它仍在運行)

+1

感謝您的回答....這聽起來很符合邏輯...我會試試....感謝 –

+0

很酷!請讓我知道這對你有沒有用。 – peshkira

0

試試這個:

public class MyTask extends AsyncTask<Void, Void, Void> { 

    private volatile boolean running = true; 
    private final ProgressDialog progressDialog; 

    public MyTask(Context ctx) { 
     progressDialog = gimmeOne(ctx); 

     progressDialog.setCancelable(true); 
     progressDialog.setOnCancelListener(new OnCancelListener() { 
      @Override 
      public void onCancel(DialogInterface dialog) { 
       // actually could set running = false; right here, but I'll 
       // stick to contract. 
       cancel(true); 
      } 
     }); 

    } 

    @Override 
    protected void onPreExecute() { 
     progressDialog.show(); 
    } 

    @Override 
    protected void onCancelled() { 
     running = false; 
    } 

    @Override 
    protected Void doInBackground(Void... params) { 

     while (running) { 
      // does the hard work 
     } 
     return null; 
    } 

    // ... 

} 

Courtesy和更多的細節看到這個答案。

+1

這是正確的,但它會允許用戶總是取消作業,並且不會在30秒後自動取消它。 – peshkira

+1

你可以在後臺使用一個定時器做一些事情,比如設置一些時間,比如說30秒,然後在那個帖子後面取消()。 。它會在任何時候停止你的asynctask – Shruti

+0

@Shruti:謝謝你的參考,但我不想讓用戶取消操作。 「取消(真)」似乎不適用於我。我怎樣才能使用get(long timeout,TimeUnit unit);''''''聽起來更加可行。 –

0

簡短的答案是你不能取消一個AsyncTask一旦開始。你可以做什麼,就是在doInBackGround()內部插入一個循環,它將檢查isCancelled(),並且如果在將來的某個時間它被設置爲真 - 從函數返回一個值(如果你已經定義了它,它將會調用);

請注意,僅僅因爲無法停止AsyncTask並不意味着操作系統在內存不足時不會取消它。如果您在AsyncTask中執行基本任務(您希望執行100%的任務),則應該記住這一點。如果是這樣,最好使用Service - 一個根據需要由操作系統自動終止並重新啓動的組件。

+1

實際上,我通過套接字連接從外部傳感器收集數據,有時候是因爲連接問題,或者如果傳感器處於脫機狀態,此過程可能需要很長時間,這就是爲什麼我想要中斷任務的原因。你說它不能被取消,爲什麼我們有'cancel(布爾狀態);獲得(長超時,TimeUnit單位);'方法可用? –

1

I會將其轉化爲異步/等待問題,使所有昂貴的方法成爲異步方法。

首先,修改DataCollector的collectData(query)以collectDataAsync(query)。 (如果你不能修改DataCollector,那麼可以將它包裝在一個lambda函數或類似的東西中)。

其次,doInBackground作爲異步任務,像這樣的東西改變:

protected async Task<String> doInBackgroundAsync(String... args) 
{ 
    DataCollector dc = new DataCollector(); 
    int timeout = 1000; 
    var task = dc.collectDataAsync(query); 
    if (await Task.WhenAny(task, Task.Delay(timeout)) == task) { 
     // task completed within timeout 
     data = task.Result; 
    } else { 
     // timeout logic 
    } 
} 

基本上,你有內部doInBackgroundAsync兩個任務:collectDataAsync和延遲任務。 您的代碼等待更快。然後你知道哪一個是,你可以做出相應的反應。

如果您還需要取消collectDataAsync任務,那麼您想使用一個cancellationToken。 我用這個來解決你的問題https://stackoverflow.com/a/11191070/3307066

請注意,現在doInBackgroundAsync是一個異步,所以它改變了一點使用它的方式。

希望它有幫助。

+0

如果你認爲這個問題是重複的,你應該標記爲重複,而不是發佈到另一個問題的鏈接。 –

+0

我修改了我的答案。我不認爲是重複的,希望現在清楚。 – jbarrameda