You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 10:18:02