使用iText 7.1.17库写入PDF时应用崩溃闪退求助
问题描述
使用iText 7.1.17库在Android上写入PDF时应用崩溃闪退,仅生成空白PDF。已添加存储权限,尝试更换内置字体、更换库版本均无效;切换PDFBox出现严重库冲突,系统内置库不支持水印和密码功能。获取到的错误日志如下(注:该日志来自编辑器而非运行时):
依赖配置
dependencies { implementation 'androidx.appcompat:appcompat:1.7.0-alpha01' implementation 'com.google.android.material:material:1.8.0-rc01' implementation 'androidx.constraintlayout:constraintlayout:2.2.0-alpha05' implementation 'com.itextpdf:kernel:7.1.17' implementation 'com.itextpdf:layout:7.1.17' implementation 'com.itextpdf:io:7.1.17' }
实现代码
public class MainActivity extends AppCompatActivity { private static final int REQUEST_WRITE_PERMISSION = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_PERMISSION); } else { pdfWriter(); } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_WRITE_PERMISSION) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { pdfWriter(); } else { System.out.println("Permission Denied!"); } } } public void pdfWriter() { PdfWriter writer = null; PdfDocument pdf = null; Document document = null; try { String pdfPath = getExternalFilesDir(null) + "/invoice.pdf"; writer = new PdfWriter(pdfPath); pdf = new PdfDocument(writer); document = new Document(pdf); document.add(new Paragraph("Sales Invoice") .setFontSize(18) .setBold() .setMarginBottom(20)); document.add(new Paragraph("Seller: ABC Store\nAddress: Main Street, City\nPhone: 123456789") .setFontSize(12) .setMarginBottom(10)); document.add(new Paragraph("Customer: John Smith\nAddress: Future District, City\nPhone: 987654321") .setFontSize(12) .setMarginBottom(20)); float[] columnWidths = {50, 200, 100, 100}; Table table = new Table(columnWidths); table.addCell(new Cell().add(new Paragraph("No.").setBold())); table.addCell(new Cell().add(new Paragraph("Product").setBold())); table.addCell(new Cell().add(new Paragraph("Quantity").setBold())); table.addCell(new Cell().add(new Paragraph("Price").setBold())); String[][] products = { {"1", "Laptop", "2", "$1500"}, {"2", "Smartphone", "1", "$1200"}, {"3", "Keyboard", "3", "$100"} }; for (String[] product : products) { for (String data : product) { table.addCell(new Cell().add(new Paragraph(data))); } } document.add(table); document.add(new Paragraph("Total Amount: $4400") .setFontSize(14) .setBold() .setMarginTop(20)); } catch (Exception e) { e.printStackTrace(); } finally { if (document != null) { document.close(); } } }
获取到的错误日志
ERROR: Failed to perform rewrite, java.lang.RuntimeException: Rewrite cancelled. at com.tyron.completion.util.RewriteUtil.lambda$performRewrite$0(RewriteUtil.java:33) at com.tyron.completion.util.RewriteUtil$$ExternalSyntheticLambda0.call(D8$$SyntheticClass:0) at com.google.common.util.concurrent.TrustedListenableFutureTask$TrustedFutureInterruptibleAsyncTask.runInterruptibly(TrustedListenableFutureTask.java:167) at com.google.common.util.concurrent.TrustedListenableFutureTask$TrustedFutureInterruptibleAsyncTask.runInterruptibly(TrustedListenableFutureTask.java:150) at com.google.common.util.concurrent.InterruptibleTask.run(InterruptibleTask.java:74) at com.google.common.util.concurrent.TrustedListenableFutureTask.run(TrustedListenableFutureTask.java:82) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) at java.lang.Thread.run(Thread.java:764) ERROR: Failed to perform rewrite, java.lang.RuntimeException: Rewrite cancelled. at com.tyron.completion.util.RewriteUtil.lambda$performRewrite$0(RewriteUtil.java:33) at com.tyron.completion.util.RewriteUtil$$ExternalSyntheticLambda0.call(D8$$SyntheticClass:0) at com.google.common.util.concurrent.TrustedListenableFutureTask$TrustedFutureInterruptibleAsyncTask.runInterruptibly(TrustedListenableFutureTask.java:167) at com.google.common.util.concurrent.TrustedListenableFutureTask$TrustedFutureInterruptibleAsyncTask.runInterruptibly(TrustedListenableFutureTask.java:150) at com.google.common.util.concurrent.InterruptibleTask.run(InterruptibleTask.java:74) at com.google.common.util.concurrent.TrustedListenableFutureTask.run(TrustedListenableFutureTask.java:82) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) at java.lang.Thread.run(Thread.java:764)
解决方案
1. 将PDF写入操作移至子线程
Android禁止在主线程执行耗时IO操作,这是导致崩溃的核心原因。修改代码,用线程执行pdfWriter():
// 替换原调用pdfWriter()的地方 new Thread(() -> { pdfWriter(); // 操作完成后可通过runOnUiThread更新UI runOnUiThread(() -> { Toast.makeText(MainActivity.this, "PDF生成完成", Toast.LENGTH_SHORT).show(); }); }).start();
2. 完善异常处理,捕获真实运行错误
原日志是编辑器的重写错误,并非运行时崩溃日志。修改catch块,将错误信息输出到Logcat并提示用户:
catch (Exception e) { Log.e("PDF_ERROR", "生成PDF失败", e); runOnUiThread(() -> { Toast.makeText(MainActivity.this, "生成PDF失败:" + e.getMessage(), Toast.LENGTH_LONG).show(); }); }
3. 优化资源关闭逻辑
虽然document.close()会自动关闭PdfDocument和PdfWriter,但确保在异常情况下资源正确释放,可调整finally块:
finally { try { if (document != null) { document.close(); } } catch (Exception e) { Log.e("PDF_ERROR", "关闭文档失败", e); } }
4. 调整依赖与权限
- 从Android 10(API 29)开始,
getExternalFilesDir(null)属于应用私有外部存储,无需申请WRITE_EXTERNAL_STORAGE权限,可移除权限申请代码,避免不必要的权限请求。 - 建议使用iText 7的最新稳定版,替换旧版本依赖,比如:
implementation 'com.itextpdf:kernel:7.2.5' implementation 'com.itextpdf:layout:7.2.5' implementation 'com.itextpdf:io:7.2.5'
内容的提问来源于stack exchange,提问作者user24060829
相关产品推荐
相关产品推荐

