在背景執行緒中直接操作 UI 元件(如 ListView.setAdapter())會拋出 CalledFromWrongThreadException,導致應用崩潰並洩漏視窗。
public void progressBar() {
progress = ProgressDialog.show(this, "title", "message", true);
new Thread(new Runnable() {
public void run() {
displayView(); // 錯誤:在背景執行緒操作 UI
runOnUiThread(new Runnable() {
public void run() { progress.dismiss(); }
});
}
}).start();
}
public void displayView() {
listAdapter = new ArrayAdapter<>(this, ..., handler.getRetrieveData());
listView = (ListView) findViewById(R.id.listView2);
listView.setAdapter(listAdapter); // 崩潰點
}
錯誤訊息:Only the original thread that created a view hierarchy can touch its views
將所有 UI 操作移至 runOnUiThread 或 Handler 中執行:
public void progressBar() {
progress = ProgressDialog.show(this, "title", "message", true);
new Thread(new Runnable() {
public void run() {
// 1. 背景執行緒:僅做耗時運算(資料庫查詢、網路請求等)
final List<String> data = handler.getRetrieveData(); // 假設此為耗時操作
// 2. 切回 UI 執行緒更新介面
runOnUiThread(new Runnable() {
public void run() {
displayView(data); // 傳入資料
progress.dismiss();
}
});
}
}).start();
}
public void displayView(List<String> data) {
listAdapter = new ArrayAdapter<>(this, R.layout.listview_format, R.id.item_list, data);
listView = (ListView) findViewById(R.id.listView2);
listView.setAdapter(listAdapter);
listView.setOnItemClickListener(...);
}
// 使用 AsyncTask (API 30 棄用,但仍可用)
private class LoadDataTask extends AsyncTask<Void, Void, List<String>> {
@Override protected void onPreExecute() { progress.show(); }
@Override protected List<String> doInBackground(Void... voids) {
return handler.getRetrieveData(); // 背景執行緒
}
@Override protected void onPostExecute(List<String> data) {
displayView(data);
progress.dismiss();
}
}
// Kotlin 協程 (推薦)
lifecycleScope.launch(Dispatchers.IO) {
val data = withContext(Dispatchers.IO) { handler.getRetrieveData() }
withContext(Dispatchers.Main) {
displayView(data)
progress.dismiss()
}
}