0

I'm trying to implement PdfViewer with Pdf Renderer Api and Jetpack Compose. Here is my code:

@Composable
fun PdfViewer(uri: Uri) {
    val context = LocalContext.current
    val rendererScope = rememberCoroutineScope()
    val mutex = remember { Mutex() }
    val renderer by produceState<PdfRenderer?>(null, uri) {
        rendererScope.launch(Dispatchers.IO) {
            val fileDescriptor =
                context.contentResolver.openFileDescriptor(uri, "r") ?: return@launch
            value = PdfRenderer(fileDescriptor)
        }
        awaitDispose {
            val currentRenderer = value
            rendererScope.launch(Dispatchers.IO) {
                mutex.withLock { currentRenderer?.close() }
            }
        }
    }
    val imageLoadingScope = rememberCoroutineScope()
    BoxWithConstraints(
        Modifier
            .fillMaxWidth()
            .background(Color.Gray)
    ) {
        val width = with(LocalDensity.current) { maxWidth.toPx() }.toInt()
        val height = (width * sqrt(2f)).toInt()
        val pageCount by remember(renderer) { derivedStateOf { renderer?.pageCount ?: 0 } }

        LazyColumn(
            verticalArrangement = Arrangement.spacedBy(10.dp)
        ) {
            items(
                count = pageCount,
                key = { index -> "$uri:$index" }
            ) { index ->
                var bitmap by remember { mutableStateOf<Bitmap?>(null) }
                if (bitmap == null) {
                    DisposableEffect(uri, index) {
                        val job = imageLoadingScope.launch(Dispatchers.IO) {
                            val destinationBitmap =
                                Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
                            mutex.withLock {
                                if (!coroutineContext.isActive) return@launch
                                try {
                                    renderer?.let {
                                        it.openPage(index).use { page ->
                                            page.render(
                                                destinationBitmap,
                                                null,
                                                null,
                                                PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY
                                            )
                                        }
                                    }
                                } catch (e: Exception) {
                                    return@launch
                                }
                            }
                            bitmap = destinationBitmap
                        }
                        onDispose { job.cancel() }
                    }
                    Box(
                        modifier = Modifier
                            .background(Color.White)
                            .aspectRatio(1f / sqrt(2f))
                            .fillMaxWidth()
                    )
                } else {
                    bitmap?.let {
                        Image(
                            modifier = Modifier
                                .background(Color.White)
                                .aspectRatio(1f / sqrt(2f))
                                .fillMaxWidth(),
                            contentScale = ContentScale.Fit,
                            bitmap = it.asImageBitmap(),
                            contentDescription = "page with index = $index"
                        )
                    }
                }
            }
        }
    }
}

When I open the first pdf document everything works fine, however if I call the fun PdfViewer(uri: Uri) again with a new Uri to open another pdf document, the first page doesn't load because I get an exception when i try to open the page (openPage):

java.lang.IllegalStateException: Already closed android.graphics.pdf.PdfRenderer.throwIfClosed(PdfRenderer.java:273)

Please tell me how can I fix this?

P.S. If I comment out this part of the code, then everything works correctly, but I'm not sure if this is correct.

   awaitDispose {
            val currentRenderer = value
            rendererScope.launch(Dispatchers.IO) {
                mutex.withLock { currentRenderer?.close() }
            }
        }
testivanivan
  • 967
  • 13
  • 36

0 Answers0