Android解压1.5GB压缩包仅完成100MB即崩溃,求排查方案
解压大压缩包崩溃的排查与修复方案
核心问题排查点及修复建议
1. 频繁调用publishProgress导致主线程负载过高
当前代码每读取4KB数据就触发一次UI更新,解压100MB数据会产生约25000次进度回调,严重阻塞主线程,引发ANR或内存崩溃。
修复方案:
改为累计读取一定大小(如1MB)或进度变化超过1%时才更新UI,减少主线程压力:
// 将进度累计变量改为long类型,避免溢出 long publishprogress = 0; long length = entry.getSize(); int lastProgress = -1; // 记录上次更新的进度,避免重复触发 int count; while ((count = zin.read(buffer)) != -1) { out.write(buffer, 0, count); publishprogress += count; int currentProgress = (int) (publishprogress * 100 / length); // 仅当进度发生变化时才更新UI if (currentProgress != lastProgress) { publishProgress(currentProgress); lastProgress = currentProgress; System.out.println("DEBUGLOG_19 " + count + "-------publishprogress " + currentProgress + " "+ entry.getName()); } }
2. int类型溢出风险
publishprogress使用int类型,若解压单个接近2GB的文件(int最大值为2^31-1≈2GB),会导致数值溢出,引发进度计算错误或异常。
修复方案:
将publishprogress声明为long类型,与entry.getSize()的long类型匹配,避免溢出。
3. 流资源未安全释放
手动调用close()方法时,若中途抛出异常,流资源可能无法正常关闭,长期运行会导致资源泄漏。
修复方案:
使用Java的try-with-resources语法自动管理流资源,确保资源正常释放:
// 自动管理ZipInputStream try (ZipInputStream zin = new ZipInputStream(new FileInputStream(file))) { ZipEntry entry; String name, dir; while ((entry = zin.getNextEntry()) != null) { name = entry.getName(); if (entry.isDirectory()) { mkdirs(outdir, name); continue; } dir = dirpart(name); if (dir != null) { mkdirs(outdir, dir); } byte[] buffer = new byte[4096]; // 自动管理BufferedOutputStream try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(new File(outdir, name)))) { long publishprogress = 0; long length = entry.getSize(); int lastProgress = -1; int count; while ((count = zin.read(buffer)) != -1) { out.write(buffer, 0, count); publishprogress += count; int currentProgress = (int) (publishprogress * 100 / length); if (currentProgress != lastProgress) { publishProgress(currentProgress); lastProgress = currentProgress; System.out.println("DEBUGLOG_19 " + count + "-------publishprogress " + currentProgress + " "+ entry.getName()); } } System.out.println("DEBUGLOG_05"); } } System.out.println("DEBUGLOG_06"); } catch (IOException e) { e.printStackTrace(); writeLog(e); }
4. 路径分隔符处理错误
dirpart方法使用File.separator,但Zip包内的路径分隔符固定为/,若设备系统分隔符不同,会导致目录创建失败。
修复方案:
统一使用/分割Zip entry路径:
private String dirpart(String name) { int index = name.lastIndexOf("/"); return index == -1 ? null : name.substring(0, index); }
5. 解压失败仍删除原压缩包
onPostExecute中无论解压是否成功都会删除压缩包,导致用户无法重试。
修复方案:
仅在解压成功时删除压缩包,通过doInBackground返回状态标识:
@Override protected String doInBackground(String... params) { try { // ... 原有解压逻辑 return "success"; } catch (IOException e) { e.printStackTrace(); writeLog(e); return "fail"; } } @Override protected void onPostExecute(String result) { loading.setVisibility(View.INVISIBLE); progressloading.setVisibility(View.INVISIBLE); if ("success".equals(result)) { com.luxury.launcher.other.Utils.delete(new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/game.zip")); StartGame(); } else { // 可添加解压失败的提示逻辑 } }
6. 额外排查项
- 磁盘空间检查:解压前确认目标目录可用空间是否足够(1.5GB压缩包解压后通常更大,建议预留2倍以上空间):
File outdir = new File(destinationPath); long freeSpace = outdir.getFreeSpace(); long requiredSpace = new File(filePath).length() * 2; if (freeSpace < requiredSpace) { // 提示用户磁盘空间不足 return "no_space"; }
- AsyncTask局限性:长时间解压任务建议改用
Coroutine或WorkManager,避免系统因进程优先级回收导致任务中断。
内容的提问来源于stack exchange,提问作者Максим Белый
相关产品推荐
相关产品推荐

