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

Android WebView转PDF部分设备生成文件损坏问题求助

WebView转PDF后部分设备发送WhatsApp显示损坏的问题排查与修复

核心问题分析

问题出在PDF生成的完整性、文件IO处理、系统权限/存储特性这几个维度,时好时坏的表现说明是非确定性的IO或异步加载问题,和设备的存储机制、WebView加载状态绑定。

具体排查与修复方案

1. 修复ParcelFileDescriptor的资源泄漏与文件写入完整性

当前代码中打开的ParcelFileDescriptor未被明确关闭,Android 13及部分厂商的存储优化会导致文件句柄未释放时,文件内容未完全刷入磁盘,生成损坏的PDF。修改文件创建逻辑,确保资源正确处理:

private ParcelFileDescriptor getOutputFile(File path, String fileName) throws IOException {
    if (!path.exists()) {
        // 确保目录创建成功,避免mkdirs失败的情况
        if (!path.mkdirs()) {
            throw new IOException("Failed to create directory: " + path.getAbsolutePath());
        }
    }
    File file = new File(path, fileName);
    // 先删除旧文件,避免覆盖时的文件残留
    if (file.exists()) {
        file.delete();
    }
    if (!file.createNewFile()) {
        throw new IOException("Failed to create file: " + file.getAbsolutePath());
    }
    return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);
}

同时,在onWriteFinished回调中,优先检查文件是否存在且大小合理,替代仅判断pages.length的逻辑:

@Override
public void onWriteFinished(PageRange[] pages) {
    super.onWriteFinished(pages);
    File file = new File(path, fileName);
    if (file.exists() && file.length() > 100) { // 最小PDF文件大小阈值
        callback.success(file.getAbsolutePath());
    } else {
        callback.onFailure("PDF file is invalid or empty");
    }
}

2. 确保WebView内容完全加载后再打印

WebView的onPageFinished仅代表HTML文档加载完成,但图片、样式等资源可能还在加载中,此时打印会导致PDF内容缺失或结构异常。需要监听WebView的加载进度,确保所有资源加载完成:

webView.setWebChromeClient(new WebChromeClient() {
    @Override
    public void onProgressChanged(WebView view, int newProgress) {
        super.onProgressChanged(view, newProgress);
        if (newProgress == 100) {
            // 所有资源加载完成,触发PDF打印
            startPrintWebView();
        }
    }
});

// 禁用WebView缓存,避免旧内容干扰
webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);

3. 适配Android 13及厂商的存储机制

Android 13的分区存储限制,以及华为、三星的定制存储优化,可能导致外部存储写入延迟或权限异常。建议:

  • 优先使用应用私有目录生成PDF(context.getFilesDir()),避免外部存储的权限和同步问题;
  • 生成PDF后,再复制到共享目录(如MediaStore.Downloads)用于分享,确保文件写入完成后再发送:
// 生成PDF到私有目录
File privateDir = getFilesDir();
File pdfFile = new File(privateDir, "temp.pdf");
// 执行打印逻辑...

// 复制到共享目录用于分享
ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DISPLAY_NAME, "document.pdf");
values.put(MediaStore.MediaColumns.MIME_TYPE, "application/pdf");
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);

Uri uri = getContentResolver().insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values);
if (uri != null) {
    try (OutputStream outputStream = getContentResolver().openOutputStream(uri);
         FileInputStream inputStream = new FileInputStream(pdfFile)) {
        byte[] buffer = new byte[1024];
        int length;
        while ((length = inputStream.read(buffer)) > 0) {
            outputStream.write(buffer, 0, length);
        }
        // 复制完成后,使用uri分享
        sharePdf(uri);
    } catch (IOException e) {
        Log.e(TAG, "Failed to copy PDF to shared directory", e);
    }
}

4. 显式配置PrintAttributes,避免设备兼容问题

默认的PrintAttributes可能在不同设备上生成不兼容的PDF结构,显式指定打印参数:

PrintAttributes printAttributes = new PrintAttributes.Builder()
        .setMediaSize(PrintAttributes.MediaSize.ISO_A4)
        .setResolution(new PrintAttributes.Resolution("pdf", "PDF", 600, 600))
        .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
        .build();
PdfPrint pdfPrint = new PdfPrint(printAttributes);

5. 添加PDF生成后的完整性校验

生成PDF后,验证文件头是否符合PDF规范(PDF文件头为%PDF-),避免发送损坏的文件:

File pdfFile = new File(path, fileName);
try (FileInputStream fis = new FileInputStream(pdfFile)) {
    byte[] header = new byte[5];
    fis.read(header);
    if (!new String(header).equals("%PDF-")) {
        callback.onFailure("Invalid PDF file");
        return;
    }
} catch (IOException e) {
    callback.onFailure("Failed to validate PDF");
    return;
}
callback.success(pdfFile.getAbsolutePath());

额外建议

  • 卸载重装能解决问题,说明是缓存或旧文件残留导致的异常,建议在每次打印前删除旧的PDF文件;
  • 避免在后台线程直接触发WebView打印,确保操作在主线程执行;
  • 针对三星、华为设备,测试时关闭系统的"存储优化"或"省电模式",观察是否能复现问题,确认是否是系统优化导致的文件写入延迟。

内容的提问来源于stack exchange,提问作者Tolgahan Tutar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 09:57:24