Android(Java)加载表格数据时显示进度条及卡顿崩溃问题求解
问题核心
当前AsyncTask的用法完全没有利用子线程处理负载:doInBackground本身运行在子线程,但你通过runOnUiThread把整个showAttendanceTable的逻辑全部切回主线程执行,数据库查询、大量View实例化、Excel生成等重负载操作全部阻塞主线程,所以会出现跳帧提示,数据量过大时直接内存不足崩溃。
修复方案
1. 拆分业务逻辑
把逻辑拆为两类,严格限定执行线程:
- 子线程执行(无UI操作):数据库查询、Excel生成、所有表格数据预计算
- 主线程执行(仅UI操作):创建View、设置样式、添加到布局
2. 修正AsyncTask实现
首先定义数据承载类,用于子线程处理完数据后传递给主线程渲染:
// 单元格预计算信息 class CellInfo { String text; int bgColor; boolean isBold; } // 整体表格预计算数据 class TableResult { int dayInMonth; int rowCount; List<String> rollList; List<String> nameList; List<List<CellInfo>> cellData; HSSFSheet generatedExcelSheet; }
修正后的AsyncTask代码:
public class MyWorker extends AsyncTask <String, Integer, TableResult> { private Context context; private ProgressDialog progressDialog; public MyWorker (Context context) { this.context = context; progressDialog = new ProgressDialog(context); progressDialog.setCancelable(false); progressDialog.setMessage("正在加载数据..."); progressDialog.setTitle("请稍候"); progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); progressDialog.setMax(100); } @Override protected void onPreExecute() { progressDialog.show(); } @Override protected TableResult doInBackground(String ... params) { TableResult result = new TableResult(); int dayInMonth = getDayInMonth(month_year); int rowSize = sid_array.length + 1; result.dayInMonth = dayInMonth; result.rowCount = rowSize; result.rollList = new ArrayList<>(); result.nameList = new ArrayList<>(); result.cellData = new ArrayList<>(); // 初始化Excel HSSFSheet hssfSheet = null; try { hssfSheet = hssfWorkbook.createSheet(month_year); } catch (IllegalArgumentException e){ e.printStackTrace(); } result.generatedExcelSheet = hssfSheet; HSSFRow hssfRow = hssfSheet.createRow(0); hssfRow.createCell(0).setCellValue("学号"); hssfRow.createCell(1).setCellValue("姓名"); // 预计算表头数据 List<CellInfo> headerCells = new ArrayList<>(); result.rollList.add("学号"); result.nameList.add("姓名"); for (int i = 1; i <= dayInMonth; i++) { String date = i < 10 ? "0" + i : String.valueOf(i); CellInfo cell = new CellInfo(); cell.text = date; cell.isBold = true; headerCells.add(cell); hssfRow.createCell(i+1).setCellValue(date); // 更新进度 publishProgress((int) (i * 1.0 / dayInMonth * 20)); } result.cellData.add(headerCells); // 预计算每行数据、批量查询数据库 for (int i = 1; i < rowSize; i++) { result.rollList.add(roll_array[i-1]); result.nameList.add(name_array[i-1]); hssfRow = hssfSheet.createRow(i); hssfRow.createCell(0).setCellValue(roll_array[i-1]); hssfRow.createCell(1).setCellValue(name_array[i-1]); int count = 0; List<CellInfo> rowCells = new ArrayList<>(); for (int j = 1; j <= dayInMonth; j++) { String day = j < 10 ? "0" + j : String.valueOf(j); String month = month_year.substring(0,3); String year = month_year.substring(4,8); String status = myDb.getStatus(sid_array[i-1], day + " " + month + " " + year); CellInfo cell = new CellInfo(); if ("A".equals(status)) { cell.text = "A"; cell.bgColor = Color.parseColor("#EF9A9A"); hssfRow.createCell(j+1).setCellValue("A"); } else if ("P".equals(status)) { count++; cell.text = String.valueOf(count); cell.bgColor = Color.parseColor("#A5D6A7"); hssfRow.createCell(j+1).setCellValue(String.valueOf(count)); } rowCells.add(cell); } result.cellData.add(rowCells); // 更新进度 publishProgress(20 + (int) (i * 1.0 / rowSize * 70)); } return result; } @Override protected void onProgressUpdate(Integer... values) { progressDialog.setProgress(values[0]); } @Override protected void onPostExecute(TableResult result) { if(progressDialog != null && progressDialog.isShowing()){ progressDialog.dismiss(); } // 主线程仅渲染View renderTable(result); } }
新增UI渲染方法:
private void renderTable(TableResult result) { TableLayout tableLayout = findViewById(R.id.table_layout); tableLayout.removeAllViews(); for (int i = 0; i < result.rowCount; i++) { TableRow row = new TableRow(this); // 行背景样式 if (i == 0) { row.setBackgroundColor(Color.parseColor("#FFD400")); } else if (i % 2 == 0) { row.setBackgroundColor(Color.parseColor("#EEEEEE")); } else { row.setBackgroundColor(Color.parseColor("#E4E4E4")); } // 学号列 TextView rollTv = new TextView(this); rollTv.setText(result.rollList.get(i)); rollTv.setPadding(20,12,20,12); rollTv.setTextSize(1, i == 0 ? 22 : 20); rollTv.setTypeface(rollTv.getTypeface(), i == 0 ? Typeface.BOLD : Typeface.NORMAL); rollTv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); rollTv.setTextColor(Color.parseColor("#FF000000")); row.addView(rollTv); // 姓名列 TextView nameTv = new TextView(this); nameTv.setText(result.nameList.get(i)); nameTv.setPadding(20,12,20,12); nameTv.setTextSize(1, i == 0 ? 22 : 20); nameTv.setTypeface(nameTv.getTypeface(), i == 0 ? Typeface.BOLD : Typeface.NORMAL); nameTv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); nameTv.setTextColor(Color.parseColor("#FF000000")); row.addView(nameTv); // 状态列 List<CellInfo> cellInfos = result.cellData.get(i); for (int j = 0; j < result.dayInMonth; j++) { CellInfo info = cellInfos.get(j); TextView statusTv = new TextView(this); statusTv.setText(info.text); statusTv.setPadding(20,12,20,12); statusTv.setTextSize(1, i == 0 ? 22 : 20); statusTv.setTypeface(statusTv.getTypeface(), info.isBold ? Typeface.BOLD : Typeface.NORMAL); statusTv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); statusTv.setTextColor(Color.parseColor("#FF000000")); if (info.bgColor != 0) { statusTv.setBackgroundColor(info.bgColor); } row.addView(statusTv); } tableLayout.addView(row); } tableLayout.setShowDividers(TableLayout.SHOW_DIVIDER_MIDDLE); }
3. 额外性能优化建议
- 数据量超过50行的话,不要使用TableLayout,换成
RecyclerView做视图复用,仅渲染屏幕可见区域的内容,内存占用可降低90%以上 - 把循环内单次调用的
myDb.getStatus改为批量查询所有考勤数据再内存匹配,查询速度可提升数倍
内容的提问来源于stack exchange,提问作者TheZenithCoder
相关产品推荐
相关产品推荐

