在 AsyncTask 執行耗時網路連線時,使用者點擊取消按鈕需中斷連線並停止後續邏輯。
asyncTask.cancel(true):true 表示嘗試中斷執行緒(呼叫 Thread.interrupt())。isCancelled():在 doInBackground 中定期檢查,若為 true 應提前返回。onCancelled():取消完成後在 UI 執行緒回呼,替代 onPostExecute。
private ConnectTask connectTask;
public void startConnect(View view) {
connectTask = new ConnectTask();
connectTask.execute(urlString);
}
public void cancelConnect(View view) {
if (connectTask != null && connectTask.getStatus() == AsyncTask.Status.RUNNING) {
connectTask.cancel(true); // 請求取消
}
}
private class ConnectTask extends AsyncTask {
private HttpURLConnection connection;
@Override
protected void onPreExecute() {
// UI 提示「連線中...」
}
@Override
protected String doInBackground(String... params) {
try {
URL url = new URL(params[0]);
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(10_000);
connection.setReadTimeout(30_000);
// 關鍵:連線前檢查取消
if (isCancelled()) return null;
connection.connect();
// 關鍵:讀取資料流中定期檢查
try (InputStream in = connection.getInputStream();
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
byte[] buffer = new byte[8192];
int len;
while ((len = in.read(buffer)) != -1) {
if (isCancelled()) return null; // 支援中途取消
out.write(buffer, 0, len);
}
return out.toString("UTF-8");
}
} catch (IOException e) {
if (!isCancelled()) Log.e("ConnectTask", "IO Error", e);
return null;
} finally {
if (connection != null) connection.disconnect();
}
}
@Override
protected void onPostExecute(String result) {
if (result != null) {
// 顯示結果
}
}
@Override
protected void onCancelled() {
// UI 提示「已取消」
}
}
isCancelled() **必須在耗時迴圈/阻塞點前檢查**,否則取消請求無法即時響應。cancel(true) 僅發送中斷信號,**無法強制停止原生 I/O 阻塞**(如 connection.connect()、in.read())。若需強制中斷網路,須在 onCancelled 或 finally 中呼叫 connection.disconnect() 關閉 Socket。AsyncTask 已棄用,新專案請改用 Coroutines + Retrofit/OkHttp、RxJava 或 ExecutorService + Future,取消語意更清晰(job.cancel()、disposable.dispose()、future.cancel(true))。
private var connectJob: Job? = null
fun startConnect() {
connectJob = lifecycleScope.launch(Dispatchers.IO) {
try {
val result = downloadUrl(urlString) // 可掛起函式,支援協程取消
withContext(Dispatchers.Main) { showResult(result) }
} catch (e: CancellationException) {
showCancelled() // 協程取消自動拋出
throw e
} catch (e: Exception) {
showError(e)
}
}
}
fun cancelConnect() {
connectJob?.cancel() // 一行搞定,包含子協程、網路呼叫皆可響應
}
掌握 cancel() + isCancelled() + disconnect() 三部曲,即可在 AsyncTask 時代優雅處理取消;長期請遷移至協程體系。