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

Android AsyncTask 變量遮蔽導致列表為空:doInBackground 內部重新聲明成員變量

     編輯:程式設計綜合問答

AsyncTask 中 doInBackground 內部重新聲明 List listContents,導致局部變量遮蔽成員變量,成員變量始終為 null,onPostExecute 讀取時為空。

錯誤代碼


public class MyActivity extends Activity {
    List listContents; // 成員變量
    
    class AsyncPost extends AsyncTask {
        @Override
        protected String doInBackground(String... params) {
            // ❌ 這裡重新聲明了 listContents,遮蔽了外部成員變量
            List listContents = new ArrayList<>(length); 
            for (int i = 0; i < length; i++) {
                listContents.add(json_data.getString("client_name"));
            }
            return "ok";
        }
        
        @Override
        protected void onPostExecute(String result) {
            // 這裡訪問的是成員變量 listContents,始終為 null
            myListView.setAdapter(new ArrayAdapter<>(..., listContents)); // NPE 或空列表
        }
    }
}

修復方案:賦值而非聲明


@Override
protected String doInBackground(String... params) {
    // ✅ 直接賦值給成員變量,不重新聲明類型
    listContents = new ArrayList<>(length); 
    for (int i = 0; i < length; i++) {
        listContents.add(json_data.getString("client_name"));
    }
    return "ok";
}

更好的現代寫法:避免成員變量,用返回值傳遞


class AsyncPost extends AsyncTask> { // 泛型指定結果類型
    
    @Override
    protected List doInBackground(Void... voids) {
        List result = new ArrayList<>();
        // ... 填充 result
        return result; // 直接返回
    }
    
    @Override
    protected void onPostExecute(List result) {
        // 直接使用參數 result,無需成員變量
        if (result != null && !result.isEmpty()) {
            myListView.setAdapter(new ArrayAdapter<>(MyActivity.this, 
                android.R.layout.simple_list_item_1, result));
        } else {
            showError();
        }
    }
}

AsyncTask 核心避坑指南

坑點現象解決
變量遮蔽內部類聲明同名變量覆蓋外部不聲明類型直接賦值、或用返回值傳遞
內存洩漏非靜態內部類持有 Activity 引用靜態內部類 + WeakReference、或 ViewModel + 協程
配置變更丟失旋轉屏幕 Task 重複執行/結果丟失onRetainNonConfigurationInstance、或 ViewModel
異常吞沒doInBackground 拋出異常無處捕獲try-catch 包裹、返回錯誤標記、或 RxJava/coroutine 異常流
並發限制默認串行執行 (API 13+),或 THREAD_POOL_EXECUTOR明確指定 Executor、或遷移協程

現代替代:Kotlin 協程 + Flow (強烈推薦)


class MyViewModel : ViewModel() {
    private val _uiState = MutableStateFlow(UiState.Loading)
    val uiState = _uiState.asStateFlow()
    
    fun loadData() {
        viewModelScope.launch {
            try {
                val list = repository.fetchList() // 掛起函數
                _uiState.value = UiState.Success(list)
            } catch (e: Exception) {
                _uiState.value = UiState.Error(e.message)
            }
        }
    }
}

@Composable
fun MyScreen(viewModel: MyViewModel = hiltViewModel()) {
    val uiState by viewModel.uiState.collectAsState()
    when (uiState) {
        is UiState.Success -> LazyColumn { items(uiState.data) { Text(it) } }
        is UiState.Loading -> CircularProgressIndicator()
        is UiState.Error -> Text("錯誤: ${it.message}")
    }
}

遷移路徑建議

  1. 新代碼直接用協程/Flow,不再寫 AsyncTask。
  2. 2. 舊 AsyncTask 漸進式遷移:提取 doInBackground 邏輯為 suspend fun,Activity 中 lifecycleScope.launch 調用。 3. 理解「變量遮蔽」是 Java 基礎語法問題,而非 AsyncTask 特有,任何內部類/匿名類都可能發生。

AsyncTask 已在 API 30 标记为废弃。掌握其坑點是為了維護舊代碼;新項目請擁抱結構化並發(協程/Flow/RxJava)。

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