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

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>

方案优先级

  1. 先实施优化Base64内存处理,这是解决内存占用过大的根本方法;
  2. 再添加独立任务栈flag,隔离PDF查看器和应用进程;
  3. 最后根据测试情况,选择是否添加内存清理或增大堆内存的优化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 05:07:36