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

如何在用户请求时终止Android PdfDocument.writeTo()操作?

问题翻译

我通过Android PdfDocument 创建PDF文档并写入文件,createDocument() 函数在非UI线程(通过AsyncTask)中调用,用户可通过shouldStop()请求终止操作。当存在大量页面时,doc.writeTo(out)完成耗时较长,我希望能根据用户的终止请求取消这个写入操作。已知doc.writeTo会调用底层原生函数,请问有没有办法在它执行时终止?

代码示例:

void createDocument() {
    PdfDocument doc = new PdfDocument();
    for (int i = 0; i < data.getSize(); i++) {
        PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(100, 100, i + 1).create();
        PdfDocument.Page page = doc.startPage(pageInfo);
        // 在页面上绘制内容
        doc.finishPage(page);
        if (shouldStop()) return;   // 用户从其他线程请求取消时返回true
    }
    OutputStream out = new FileOutputStream(path);
    doc.writeTo(out);  // >>> 耗时操作,希望用户请求时能停止写入
    doc.close();
    out.close();
}

解决方案

直接中断PdfDocument.writeTo()的原生调用没有官方支持的安全方式,但可以通过以下两种方法实现类似效果:

1. 包装可中断的OutputStream

自定义一个OutputStream子类,在每次写入数据时检查终止请求,若触发则抛出IOException来中断writeTo()流程:

class InterruptibleOutputStream extends FilterOutputStream {
    private final Supplier<Boolean> shouldStop;

    public InterruptibleOutputStream(OutputStream out, Supplier<Boolean> shouldStop) {
        super(out);
        this.shouldStop = shouldStop;
    }

    @Override
    public void write(int b) throws IOException {
        checkIfStopped();
        super.write(b);
    }

    @Override
    public void write(byte[] b) throws IOException {
        checkIfStopped();
        super.write(b);
    }

    @Override
    public void write(byte[] b, int off, int len) throws IOException {
        checkIfStopped();
        super.write(b, off, len);
    }

    private void checkIfStopped() throws IOException {
        if (shouldStop.get()) {
            throw new IOException("Operation cancelled by user");
        }
    }
}

修改原代码中的输出流部分:

OutputStream out = new FileOutputStream(path);
try {
    InterruptibleOutputStream interruptibleOut = new InterruptibleOutputStream(out, this::shouldStop);
    doc.writeTo(interruptibleOut);
} catch (IOException e) {
    // 处理用户取消的情况,删除不完整的PDF文件
    new File(path).delete();
} finally {
    doc.close();
    out.close();
}

注意:这种方式会生成损坏的PDF文件,必须在捕获异常后删除不完整的文件,避免残留无效文件。

2. 改用协程(替代AsyncTask)

如果可以放弃AsyncTask,改用Kotlin协程,结合isActive状态检查,配合上面的InterruptibleOutputStream可以更优雅地处理取消:

fun createDocument(path: String, data: YourDataType) = viewModelScope.launch {
    val doc = PdfDocument()
    try {
        for (i in 0 until data.size) {
            val pageInfo = PdfDocument.PageInfo.Builder(100, 100, i + 1).create()
            val page = doc.startPage(pageInfo)
            // 绘制页面内容
            doc.finishPage(page)
            if (!isActive) return@launch
        }
        FileOutputStream(path).use { fileOut ->
            InterruptibleOutputStream(fileOut) { !isActive }.use { interruptibleOut ->
                doc.writeTo(interruptibleOut)
            }
        }
    } catch (e: IOException) {
        File(path).delete()
    } finally {
        doc.close()
    }
}

不推荐的方式

不要直接调用Thread.interrupt()来终止执行writeTo()的线程,因为原生PDF写入操作可能持有未释放的资源,导致内存泄漏或其他不稳定问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 07:45:51