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

Android 秒表無法停止?解決 Thread.stop() 棄用問題的正確做法

     編輯:程式設計綜合問答

問題背景

在 Android 開發中,許多初學者會嘗試使用 Thread 配合 Runnable 實作簡單的秒表功能。然而,直接呼叫 Thread.stop() 來停止執行緒是極不推薦的做法,因為此方法已被標記為 Deprecated(棄用),且會導致嚴重的線程安全問題,例如資料不一致、鎖釋放異常等。本文將分析一個典型的「秒表無法停止」案例,並提供正確的解決方案。

錯誤代碼分析

原始代碼中,開發者建立了一個 Thread 實例 currentThread,並在 run() 方法中使用 while(true) 無限迴圈,配合 sleep(1000) 實現每秒更新 UI。停止按鈕綁定了 currentThread.stop(),但這在現代 Android 版本中往往無效,甚至拋出異常。

// 錯誤示範:直接呼叫 stop()
b2.setOnClickListener(new View.OnClickListener() {
    public void onClick(View view) {
        currentThread.stop(); // 已棄用,不可靠
    }
});

核心問題

  • Thread.stop() 已棄用:自 Java 1.2 起標記為 deprecated,Android 運行時可能忽略或拋出 UnsupportedOperationException。
  • 無限迴圈缺乏退出條件:while(true) 沒有中斷判斷,導致執行緒無法優雅結束。
  • UI 更新方式不當:雖然使用 Handler 傳遞訊息更新 TextView 是可行的,但配合錯誤的停止機制會造成記憶體洩漏或 ANR。

正確解決方案:使用標誌位控制執行緒生命週期

最佳實踐是引入一個 volatile boolean 標誌位(如 shouldRun),在迴圈中檢查該標誌,並在需要停止時將其設為 false。配合 Thread.interrupt() 可打斷 sleep() 狀態,實現響應式停止。

重構後的關鍵代碼

public class StopWatchActivity extends AppCompatActivity implements Runnable {
    private TextView tvTimer;
    private Button btnStart, btnStop, btnReset;
    private Thread timerThread;
    private volatile boolean shouldRun = false; // volatile 保證可見性
    private int elapsedSeconds = 0;
    private Handler uiHandler = new Handler(Looper.getMainLooper());

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_stopwatch);
        tvTimer = findViewById(R.id.tv_timer);
        btnStart = findViewById(R.id.btn_start);
        btnStop = findViewById(R.id.btn_stop);
        btnReset = findViewById(R.id.btn_reset);

        btnStart.setOnClickListener(v -> startTimer());
        btnStop.setOnClickListener(v -> stopTimer());
        btnReset.setOnClickListener(v -> resetTimer());
    }

    private void startTimer() {
        if (timerThread == null || !timerThread.isAlive()) {
            shouldRun = true;
            timerThread = new Thread(this);
            timerThread.start();
        }
    }

    private void stopTimer() {
        shouldRun = false;
        if (timerThread != null) {
            timerThread.interrupt(); // 打斷 sleep,讓執行緒盡快檢查標誌位
        }
    }

    private void resetTimer() {
        stopTimer();
        elapsedSeconds = 0;
        uiHandler.post(() -> tvTimer.setText("0"));
    }

    @Override
    public void run() {
        while (shouldRun) {
            try {
                Thread.sleep(1000);
                if (!shouldRun) break; // 雙重檢查,防止 sleep 後繼續
                elapsedSeconds++;
                uiHandler.post(() -> tvTimer.setText(String.valueOf(elapsedSeconds)));
            } catch (InterruptedException e) {
                // 執行緒被 interrupt,正常退出迴圈
                Thread.currentThread().interrupt(); // 保留中斷狀態
                break;
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        stopTimer(); // 確保 Activity 銷毀時執行緒正確停止
    }
}

關鍵點總結

  1. volatile 關鍵字:確保多執行緒間 shouldRun 的可見性,讀取到最新值。
  2. interrupt() 配合 InterruptedException:主動打斷阻塞操作(sleep/wait),避免等待 1 秒才檢查標誌位。
  3. 在 onDestroy 中清理:防止 Activity 銷毀後執行緒持續運行導致記憶體洩漏或崩潰。
  4. 避免直接操作 UI 執行緒:使用 Handler 或 runOnUiThread 更新 View。

進階建議:使用 Kotlin Coroutines 或 RxJava

對於現代 Android 開發,建議使用 Kotlin 協程 或 Flow 實現計時器,代碼更簡潔、結構化並發處理更安全。例如:

// Kotlin 協程寫法(簡略)
lifecycleScope.launch {
    while (isActive) {
        delay(1000)
        _elapsedSeconds.value++
    }
}

這樣自動綁定生命週期,無需手動管理執行緒停止。

結語

秒表無法停止的根本原因在於使用了已棄用且不安全的 Thread.stop()。採用 標誌位 + interrupt() 模式是 Java 基礎執行緒控制的標準做法;而在新專案中,優先考慮協程等現代異步框架,能從架構層面避免此類問題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved