首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

Android Activity 銷毀時如何優雅地停止子線程與 AsyncTask

     編輯:程式設計疑難解答

問題場景

在 Android 開發中,新手常犯的錯誤是在 Activity 中直接啟動無限循環的執行緒或 AsyncTask 進行網路輪詢。當使用者離開頁面(onDestroy)時,這些後台任務若未正確取消,會繼續執行,導致:

  • 記憶體洩漏(持有 Activity 參考)
  • UI 更新崩潰(View 已分離)
  • 電量與流量浪費

錯誤代碼分析

// 問題代碼片段
new Thread() {
    public void run() {
        while (true) { // 無限循環,無退出機制
            Thread.sleep(900);
            new AnotherTask().execute("JSON"); // 額外創建 AsyncTask
        }
    }
}.start();

此處 while(true) 缺乏中斷條件,AsyncTask 亦未持有引用以便取消。

正確解決方案:標誌位控制 + 取消機制

方案一:使用 volatile 布林標誌位 (推薦用於 Thread)

public class WaterActivity extends Activity {
    private volatile boolean isRunning = false; // volatile 保證可見性
    private Thread pollThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // ... 初始化視圖 ...
        
        isRunning = true;
        pollThread = new Thread(new PollRunnable());
        pollThread.start();
    }

    class PollRunnable implements Runnable {
        @Override
        public void run() {
            while (isRunning) { // 檢查標誌位
                try {
                    Thread.sleep(900);
                    if (!isRunning) break; // 睡醒後再次檢查
                    // 在主執行緒更新 UI 或發送 Message
                    runOnUiThread(() -> new AnotherTask().execute("JSON"));
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt(); // 重置中斷狀態
                    break; // 被 interrupt 時退出
                }
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        isRunning = false; // 1. 設置標誌位
        if (pollThread != null) {
            pollThread.interrupt(); // 2. 中斷睡眠/阻塞
        }
    }
}

方案二:正確使用 AsyncTask 的 cancel 機制

注意: AsyncTask 已在 Android 11 (API 30) 棄用,建議遷移至 ExecutorService + Future 或 Kotlin Coroutines/RxJava。若維護舊專案,必須實作:

private AnotherTask currentTask;

private class AnotherTask extends AsyncTask {
    @Override
    protected String doInBackground(String... params) {
        // 定期檢查 isCancelled()
        while (!isCancelled()) { 
            // 執行耗時操作...
            if (isCancelled()) break;
        }
        return null;
    }

    @Override
    protected void onPostExecute(String result) {
        if (!isCancelled() && !isFinishing()) { // 雙重保險
            addEntry(mChart1);
            addEntry(mChart2);
        }
    }
}

@Override
protected void onDestroy() {
    super.onDestroy();
    if (currentTask != null) {
        currentTask.cancel(true); // true 表示 mayInterruptIfRunning
    }
}

最佳實踐總結

  1. 生命週期綁定: 在 onStart/onResume 啟動,onStop/onDestroy 停止。
  2. 避免匿名內部類持有外部引用: 使用 static 內部類 + WeakReference<Activity> 或 ViewBinding。
  3. 現代替代方案: 新專案請使用 Kotlin 協程 (lifecycleScope.launch { ... }) 或 Flow,自動處理生命週期取消,程式碼更簡潔安全。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved