程式碼走讀
public class MainActivity extends ActionBarActivity {
private int[] data = new int[100];
int hasData = 0, status = 0;
ProgressBar bar, bar2;
// Handler 定義:處理子執行緒送來的訊息
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == 0x111) { // 訊息類型匹配
bar.setProgress(status); // 更新進度條 1
bar2.setProgress(status); // 更新進度條 2
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bar = findViewById(R.id.bar);
bar2 = findViewById(R.id.bar2);
// 啟動子執行緒模擬耗時工作
new Thread() {
public void run() {
while (status < 100) {
status = doWork(); // 產生資料、睡眠 1 秒
mHandler.sendEmptyMessage(0x111); // 通知 UI 執行緒更新
}
}
}.start();
}
public int doWork() {
data[hasData++] = (int)(Math.random() * 100);
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
return hasData; // 返回已產生資料筆數 (0~100)
}
}
關鍵機制解析
| 元件 | 角色 | 說明 |
| 子執行緒 (`new Thread`) | 背景工作 | 執行 `doWork()`,不可直接操作 UI (會拋 `CalledFromWrongThreadException`)。 |
| `Handler` | 跨執行緒通訊 | 建立在 **UI 執行緒** (主執行緒),其 `Looper` 綁定主執行緒訊息隊列。`sendEmptyMessage` 將訊息推入隊列,`handleMessage` 在 **UI 執行緒** 執行。 |
| `Message.what = 0x111` | 訊息識別碼 | 自訂整數,用於區分不同訊息類型。此處單一類型,固定 0x111。 |
| `ProgressBar.setProgress` | UI 更新 | 必須在 UI 執行緒呼叫,`Handler` 保證此點。 |
流程圖
主執行緒 (UI) 子執行緒 (Worker)
│ │
│ 建立 Handler (綁定主 Looper) │
│─────────────────────────────────►│
│ │ while(status<100)
│ │ status = doWork()
│ │ Thread.sleep(1000)
│ │ sendEmptyMessage(0x111)
│◄─────────────────────────────────│
│ handleMessage(0x111) │
│ bar.setProgress(status) │
│ bar2.setProgress(status) │
│ │
現代替代方案
- `runOnUiThread(Runnable)`**:`runOnUiThread(() -> bar.setProgress(status));` 簡潔,無需 Handler。
- Kotlin 協程**:`lifecycleScope.launch { while(status<100) { status = doWork(); withContext(Dispatchers.Main) { bar.progress = status } delay(1000) } }`
- RxJava / Flow / LiveData**:響應式資料流自動切換執行緒。
常見坑
- 子執行緒直接 `bar.setProgress()` → 崩潰。
- `Handler` 建立在子執行緒 → `handleMessage` 仍在子執行緒執行,更新 UI 失敗。必須在 `onCreate` (主執行緒) 建立。
- 未處理 `Activity` 銷毀後訊息仍送達 → 記憶體洩漏。解決:`static class MyHandler extends Handler { WeakReference ref; ... }` 或 `removeCallbacksAndMessages(null)` 在 `onDestroy`。