如何在用户请求时终止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
相关产品推荐
相关产品推荐

