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

如何在Android Fragment中展示base64编码的PDF并实现保存打印功能

Android Base64格式PDF在Fragment内实现预览+保存/打印功能方案

效果预览

预期效果图

核心实现思路

采用Android原生PdfRenderer组件实现PDF渲染,无需提前将PDF写入公共存储即可完成预览,完全规避Android 10+分区存储权限限制,以及Android 7.0+的Uri暴露报错。预览逻辑和保存/打印逻辑完全解耦,依赖应用专属缓存目录完成临时存储,全程无需申请外部存储权限。

具体实现步骤

1. 布局配置(fragment_pdf_preview.xml)

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- PDF分页渲染容器 -->
    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/vp_pdf_pages"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <!-- 功能触发按钮 -->
    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab_option"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="24dp"
        android:src="@drawable/ic_menu_more" />

</FrameLayout>

2. PDF预览逻辑实现

预览时仅将解码后的PDF字节写入应用专属缓存目录,该目录读写无需任何权限:

// 解码Base64字符串为PDF字节数组
val pdfByteArray = Base64.decode(rawBase64PdfStr, Base64.DEFAULT)
// 写入应用专属缓存目录临时文件
val tempCacheFile = File(requireContext().cacheDir, "preview_temp.pdf").apply {
    outputStream().use { output ->
        output.write(pdfByteArray)
    }
}
// 加载PDF到PdfRenderer
val fileDescriptor = ParcelFileDescriptor.open(tempCacheFile, ParcelFileDescriptor.MODE_READ_ONLY)
val pdfRenderer = PdfRenderer(fileDescriptor)
// 为ViewPager2配置适配器,逐页渲染PdfRenderer返回的Bitmap即可完成预览

3. 保存/打印功能实现

点击FloatingActionButton点击后弹出操作菜单,分别对应两个功能:

  • 保存功能:调用系统存储访问框架(SAF)触发用户自主选择保存路径,无需申请存储权限:
// 触发保存逻辑
val saveIntent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
    addCategory(Intent.CATEGORY_OPENABLE)
    type = "application/pdf"
    putExtra(Intent.EXTRA_TITLE, "exported_file.pdf")
}
startActivityForResult(saveIntent, REQUEST_SAVE_PDF)

// onActivityResult回调中处理保存逻辑
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == REQUEST_SAVE_PDF && resultCode == Activity.RESULT_OK) {
        data?.data?.let { saveUri ->
            requireContext().contentResolver.openOutputStream(saveUri)?.use { output ->
                output.write(pdfByteArray)
            }
        }
    }
}
  • 打印功能:调用系统原生打印框架,无需将文件写入本地存储:
val printManager = requireContext().getSystemService(Context.PRINT_SERVICE) as PrintManager
val printAdapter = object : PrintDocumentAdapter() {
    override fun onLayout(
        oldAttributes: PrintAttributes?,
        newAttributes: PrintAttributes?,
        cancellationSignal: CancellationSignal?,
        callback: LayoutResultCallback?,
        extras: Bundle?
    ) {
        if (cancellationSignal?.isCanceled == true) {
            callback?.onLayoutCancelled()
            return
        }
        val info = PrintDocumentInfo.Builder("print_doc.pdf")
            .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
            .setPageCount(pdfRenderer.pageCount)
            .build()
        callback?.onLayoutFinished(info, true)
    }

    override fun onWrite(
        pages: Array<out PageRange>?,
        destination: ParcelFileDescriptor?,
        cancellationSignal: CancellationSignal?,
        callback: WriteResultCallback?
    ) {
        FileOutputStream(destination?.fileDescriptor).use { output ->
            output.write(pdfByteArray)
        }
        callback?.onWriteFinished(arrayOf(PageRange.ALL_PAGES))
    }
}
printManager.print("PDF打印任务", printAdapter, PrintAttributes.Builder().build())

原有报错说明

之前遇到的exposed beyond app through Intent.getData()报错是Android 7.0+的系统限制,禁止应用直接对外暴露file://类型的Uri,和存储权限无关。原有方案需要调用第三方应用打开PDF才会触发该报错,本方案采用原生渲染PDF,完全规避该问题。
WRITE_EXTERNAL_STORAGE权限在Android 10+分区存储机制下确实已失效,仅当应用适配兼容模式且目标Sdk版本低于29时才会生效,本方案不需要申请该权限。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 16:24:05