AsyncTask 中 doInBackground 內部重新聲明 List,導致局部變量遮蔽成員變量,成員變量始終為 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();
}
}
}
| 坑點 | 現象 | 解決 |
|---|---|---|
| 變量遮蔽 | 內部類聲明同名變量覆蓋外部 | 不聲明類型直接賦值、或用返回值傳遞 |
| 內存洩漏 | 非靜態內部類持有 Activity 引用 | 靜態內部類 + WeakReference、或 ViewModel + 協程 |
| 配置變更丟失 | 旋轉屏幕 Task 重複執行/結果丟失 | onRetainNonConfigurationInstance、或 ViewModel |
| 異常吞沒 | doInBackground 拋出異常無處捕獲 | try-catch 包裹、返回錯誤標記、或 RxJava/coroutine 異常流 |
| 並發限制 | 默認串行執行 (API 13+),或 THREAD_POOL_EXECUTOR | 明確指定 Executor、或遷移協程 |
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}")
}
}
doInBackground 邏輯為 suspend fun,Activity 中 lifecycleScope.launch 調用。
3. 理解「變量遮蔽」是 Java 基礎語法問題,而非 AsyncTask 特有,任何內部類/匿名類都可能發生。
AsyncTask 已在 API 30 标记为废弃。掌握其坑點是為了維護舊代碼;新項目請擁抱結構化並發(協程/Flow/RxJava)。