Android端Ionic Capacitor应用打开大PDF文件崩溃求助
解决Ionic Capacitor Android应用打开大PDF时崩溃的问题
问题原因分析
从Logcat报错来看,是WebView的Renderer进程因内存不足(OOM)被系统杀死,进而导致整个应用崩溃。虽然你已经把PDF生成逻辑移到原生端,但生成阶段的内存占用未优化(把完整Base64存在内存中),加上打开PDF时系统资源紧张,最终触发了OOM。
解决方案
1. 优化Base64块的内存处理(核心修复)
当前代码把所有Base64块存在StringBuilder中,100MB的PDF对应的Base64约133MB,解码时还要生成同等大小的byte数组,瞬间内存占用翻倍。改成边接收块边写入文件,避免在内存中存储完整的Base64字符串:
修改EchoPlugin代码:
@CapacitorPlugin(name = "Echo") public class EchoPlugin extends Plugin { private FileOutputStream fos; private File outputFile; @PluginMethod() public void echo(PluginCall call) throws IOException { String base64Chunk = call.getString("value"); if (base64Chunk != null) { String base64WithoutPrefix = base64Chunk.replaceFirst("data:application/pdf;base64,", ""); if (base64WithoutPrefix.equals("LAST_CHUNK_SIGNAL")) { // 处理最后一块信号,关闭文件流 if (fos != null) { try { fos.close(); } catch (IOException e) { call.reject("Failed to close file stream", e); return; } fos = null; } JSObject ret = new JSObject(); ret.put("value", outputFile.getAbsolutePath()); openPdf(outputFile.getAbsolutePath()); call.resolve(ret); } else { // 第一次接收块时初始化文件输出流 if (fos == null) { // 用时间戳命名文件,避免覆盖旧文件 outputFile = new File(getContext().getFilesDir(), "report_" + System.currentTimeMillis() + ".pdf"); fos = new FileOutputStream(outputFile); } // 解码当前块并直接写入文件,不累积内存 byte[] chunkBytes; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { chunkBytes = Base64.getDecoder().decode(base64WithoutPrefix); } else { chunkBytes = android.util.Base64.decode(base64WithoutPrefix, android.util.Base64.DEFAULT); } fos.write(chunkBytes); JSObject ret = new JSObject(); ret.put("value", "Chunk processed."); call.resolve(ret); } } else { call.reject("Invalid chunk."); } } // ... 其他代码 }
2. 让PDF查看器独立运行,避免关联WebView进程
修改openPdf方法,给Intent添加FLAG_ACTIVITY_NEW_TASK,让PDF查看器在单独的任务栈启动,即使WebView进程崩溃也不会影响应用主进程:
public void openPdf(String param) { try { Intent intent = new Intent(Intent.ACTION_VIEW); File file = new File(param); Uri fileUri = FileProvider.getUriForFile(getContext(), "com.gmail.myapp.myapp.fileprovider", file); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 新增:独立任务栈 intent.setDataAndType(fileUri, "application/pdf"); this.getContext().startActivity(intent); } catch (Exception e) { e.printStackTrace(); } }
3. 打开PDF前释放WebView内存
在调用openPdf前,手动清理WebView缓存并触发GC,减少内存占用:
// 在openPdf之前添加 if (getBridge() != null && getBridge().getWebView() != null) { getBridge().getWebView().clearCache(true); getBridge().getWebView().clearHistory(); System.gc(); // 触发垃圾回收 }
4. 可选:增大应用堆内存(治标)
在AndroidManifest.xml的<application>标签中添加android:largeHeap="true",允许应用使用更大的堆内存空间,降低OOM概率:
<application android:name=".MainApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:largeHeap="true"> <!-- 新增 --> <!-- ... 其他配置 --> </application>
方案优先级
- 先实施优化Base64内存处理,这是解决内存占用过大的根本方法;
- 再添加独立任务栈flag,隔离PDF查看器和应用进程;
- 最后根据测试情况,选择是否添加内存清理或增大堆内存的优化。
内容的提问来源于stack exchange,提问作者pilotman
相关产品推荐
相关产品推荐

