問題背景
在 AlertDialog.Builder 中啟動 AsyncTask,並嘗試在 doInBackground 啟動自訂 Thread,不確定各段代碼運行在哪個執行緒。
核心概念
| 方法/區塊 | 執行緒 | 說明 |
onPreExecute() | UI 執行緒 (Main) | 啟動前準備,可操作 UI |
doInBackground() | 背景執行緒 (SerialExecutor/THREAD_POOL_EXECUTOR) | 耗時運算,**不可操作 UI** |
onProgressUpdate() | UI 執行緒 | 呼叫 publishProgress() 觸發 |
onPostExecute() | UI 執行緒 | 背景任務完成後回調 |
新建 Thread().start() | 新獨立執行緒 | 與 AsyncTask 執行緒池無關 |
驗證代碼:列印執行緒名稱
final Thread myPrettyOperation = new Thread() {
@Override public void run() {
Log.i("THREAD_DEBUG", "Thread1: " + Thread.currentThread().getName());
// 耗時操作
}
};
class MyPrettyTask extends AsyncTask {
@Override protected Boolean doInBackground(Void... voids) {
Log.i("THREAD_DEBUG", "Thread2 (doInBackground): " + Thread.currentThread().getName());
myPrettyOperation.start(); // 啟動獨立執行緒
return true;
}
@Override protected void onPostExecute(Boolean result) {
Log.i("THREAD_DEBUG", "Thread3 (onPostExecute): " + Thread.currentThread().getName());
}
}
new MyPrettyTask().execute();
Log.i("THREAD_DEBUG", "Thread4 (main after execute): " + Thread.currentThread().getName());
典型輸出範例
I/THREAD_DEBUG: Thread4 (main after execute): main
I/THREAD_DEBUG: Thread2 (doInBackground): AsyncTask #1
I/THREAD_DEBUG: Thread1: Thread-123
I/THREAD_DEBUG: Thread3 (onPostExecute): main
關鍵觀察
- main 執行緒:
onPreExecute、onPostExecute、execute() 之後的代碼
- AsyncTask 執行緒池:
doInBackground (名稱如 AsyncTask #1)
- 自訂 Thread:獨立命名 (如
Thread-123),**不受 AsyncTask 生命週期控制**
常見誤區與修正
| 誤區 | 後果 | 修正 |
| 在 doInBackground 操作 UI | CalledFromWrongThreadException | 用 publishProgress/onProgressUpdate 或 Handler |
| 在 doInBackground 啟動 Thread 並預期同步 | AsyncTask 可能先結束,Thread 仍在跑 | 用 Thread.join() 或改用 ExecutorService/Future |
| 以為 AsyncTask 會等待子 Thread | 資料未備妥就進入 onPostExecute | 在子 Thread 完成後用 Handler 送訊息觸發後續 |
現代替代:Kotlin Coroutines / RxJava
// Coroutines 更直觀的執行緒控制
lifecycleScope.launch(Dispatchers.IO) {
val result = longRunningOperation() // 背景執行緒
withContext(Dispatchers.Main) {
updateUI(result) // 主執行緒
}
}