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

PdfRenderer.openPage抛出未预期IllegalStateException问题求助

问题描述

调用PdfRenderer.openPage时意外抛出java.lang.IllegalStateException。根据官方文档,仅当先调用PdfRenderer.close()后再打开页面才会触发该异常,但调试确认调用openPage前渲染器未被关闭。

仅在短时间内快速切换20-30页时才会触发该异常(95%场景下应用正常)。起初怀疑内存问题,但已释放旧页面,且性能分析显示内存峰值未达1GB,可排除该因素。

完整堆栈信息

FATAL EXCEPTION: pool-3-thread-2
Process: com.example.my_app, PID: 11178
java.lang.IllegalStateException: cannot load page
    at android.graphics.pdf.PdfRenderer.nativeOpenPageAndGetSize(Native Method)
    at android.graphics.pdf.PdfRenderer.-$$Nest$smnativeOpenPageAndGetSize(Unknown Source:0)
    at android.graphics.pdf.PdfRenderer$Page.<init>(PdfRenderer.java:315)
    at android.graphics.pdf.PdfRenderer$Page.<init>(Unknown Source:0)
    at android.graphics.pdf.PdfRenderer.openPage(PdfRenderer.java:233)
    at com.example.my_app.LoadedDocument.renderPage(LoadedDocument.kt:353)

相关代码

private fun renderAndSavePage(pageNumber: Int) {
    pagesSemaphore.acquire()

    // Exit early if a previous call rendered this page.
    if (pages[pageNumber].width != EMPTY_BITMAP_WIDTH) {
        pagesSemaphore.release()
        return
    }

    // If, in the time between this function call and acquiring the semaphore,
    // the user has quickly switched pages beyond where this needs to be loaded,
    // skip loading this.
    if (abs(pageNumber - metadata.pageNo) > PAGE_BUFFER) {
        pagesSemaphore.release()
        return
    }

    // Open the page and save it to a bitmap.
    val page = renderer.openPage(pageNumber)
    val ratio = page.height.toFloat() / page.width.toFloat()
    val bitmap = Bitmap.createBitmap(
        imagePixelWidth,
        (imagePixelWidth * ratio).toInt(),
        Bitmap.Config.ARGB_8888
    )
    bitmap.eraseColor(Color.WHITE)
    page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)
    pages[pageNumber] = bitmap
    page.close()
    pagesSemaphore.release()
}
问题分析与解决办法

触发原因

  1. PdfRenderer线程安全问题:PdfRenderer本身并非线程安全组件,多线程同时调用openPage会导致底层Native资源状态混乱,即使代码中调用了page.close(),快速切换时的并发操作仍可能让资源释放不及时,触发异常。
  2. 信号量控制有局限:当前的pagesSemaphore仅限制了渲染任务的并发数,但未约束PdfRenderer的访问线程,无法避免多线程对同一渲染器的竞争。
  3. 资源释放延迟:page.close()的Native层资源回收存在延迟,快速触发的openPage调用可能在旧页面资源未完全释放时执行,导致底层资源冲突。

解决办法

  1. 强制PdfRenderer单线程访问:将所有PDF渲染相关操作(openPage、render、close)放到同一个线程执行,比如用HandlerThread创建专用渲染线程,所有任务都提交到该线程队列处理,彻底避免多线程竞争。
  2. 添加异常捕获与重试逻辑:在openPage调用处捕获IllegalStateException,进行1-2次有限重试,重试前短暂休眠等待资源释放:
    val page = try {
        renderer.openPage(pageNumber)
    } catch (e: IllegalStateException) {
        Thread.sleep(50)
        renderer.openPage(pageNumber) // 仅重试一次,避免阻塞
    }
    
  3. 优化预加载与任务取消逻辑:调整PAGE_BUFFER大小减少并发渲染任务数;用协程Job或任务队列管理渲染请求,当用户快速切换页面时,取消掉已提交但未执行的非必要渲染任务,减少无效资源占用。
  4. 增加渲染器状态校验:调用openPage前检查renderer是否意外关闭,若已关闭则重新初始化:
    if (renderer.isClosed) {
        // 重新创建PdfRenderer实例,需确保FileDescriptor有效
        renderer = PdfRenderer(...)
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 07:27:36