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

Android 進度對話框:正確在背景執行緒更新 UI 避免崩潰

     編輯:程式設計綜合問答

錯誤場景

在背景執行緒中直接操作 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 / Kotlin Coroutines

// 使用 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()
    }
}

核心原則

  • 背景執行緒:只做運算、I/O、資料庫查詢
  • UI 執行緒:所有 View 操作、Adapter 設定、對話框顯示/關閉
  • 傳遞資料:背景執行緒準備好資料,再切回主執行緒套用
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved