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

Android Java中读取Byte数据展示PDF及传递Byte至PDF的方法

Android 处理API返回的PDF字节数据实现方案

一、依赖说明

  • 系统原生方案:使用PdfRenderer,支持Android 5.0(API 21+),无需额外依赖。
  • 第三方增强方案:如需要批注、搜索等功能,可使用AndroidPdfViewer库。

二、读取API返回的PDF字节数据

Retrofit 实现

定义API接口:

interface PdfApiService {
    @GET("your/pdf/endpoint")
    suspend fun fetchPdf(): ResponseBody
}

获取字节数组:

// 在ViewModel/Repository中调用
val pdfBytes = pdfApiService.fetchPdf().bytes()

OkHttp 实现

val client = OkHttpClient()
val request = Request.Builder().url("your-pdf-url").build()
val response = client.newCall(request).execute()
val pdfBytes = response.body?.bytes()

三、展示PDF

方案1:系统原生PdfRenderer

1. 将字节写入临时文件

fun writePdfToTempCache(context: Context, pdfBytes: ByteArray): File {
    val tempFile = File(context.cacheDir, "temp_${System.currentTimeMillis()}.pdf")
    FileOutputStream(tempFile).use { it.write(pdfBytes) }
    return tempFile
}

2. 实现页面渲染适配器

用ViewPager2展示多页PDF:

class PdfPageAdapter(private val renderer: PdfRenderer) : RecyclerView.Adapter<PdfPageAdapter.ViewHolder>() {

    inner class ViewHolder(val imageView: ImageView) : RecyclerView.ViewHolder(imageView)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val imageView = ImageView(parent.context).apply {
            layoutParams = ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
            )
            scaleType = ImageView.ScaleType.FIT_CENTER
        }
        return ViewHolder(imageView)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        renderer.openPage(position).use { page ->
            val bitmap = Bitmap.createBitmap(page.width, page.height, Bitmap.Config.ARGB_8888)
            page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)
            holder.imageView.setImageBitmap(bitmap)
        }
    }

    override fun getItemCount(): Int = renderer.pageCount
}

3. 在Activity中初始化

class PdfDisplayActivity : AppCompatActivity() {
    private var pdfRenderer: PdfRenderer? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_pdf_display)

        val pdfBytes = intent.getByteArrayExtra("PDF_BYTES") ?: return
        val tempFile = writePdfToTempCache(this, pdfBytes)
        val parcelFd = ParcelFileDescriptor.open(tempFile, ParcelFileDescriptor.MODE_READ_ONLY)
        pdfRenderer = PdfRenderer(parcelFd)

        findViewById<ViewPager2>(R.id.view_pager).adapter = PdfPageAdapter(pdfRenderer!!)
    }

    override fun onDestroy() {
        super.onDestroy()
        pdfRenderer?.close()
    }
}

对应布局activity_pdf_display.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.viewpager2.widget.ViewPager2
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/view_pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

方案2:第三方AndroidPdfViewer

添加依赖(build.gradle):

implementation 'com.github.barteksc:android-pdf-viewer:3.2.0-beta.1'

直接加载字节数组:

// 在Activity/Fragment中
findViewById<PDFView>(R.id.pdf_view).apply {
    fromBytes(pdfBytes)
        .defaultPage(0)
        .enableSwipe(true)
        .swipeHorizontal(false)
        .load()
}

对应布局:

<com.github.barteksc.pdfviewer.PDFView
    android:id="@+id/pdf_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

四、传递PDF字节数据

1. 组件间传递

小体积字节数组(<1MB):用Intent

val intent = Intent(this, PdfDisplayActivity::class.java)
intent.putExtra("PDF_BYTES", pdfBytes)
startActivity(intent)

大体积字节数组:用全局单例

避免TransactionTooLargeException:

// 定义全局数据 holder
object PdfDataHolder {
    var pdfBytes: ByteArray? = null
}

// 发送方
PdfDataHolder.pdfBytes = pdfBytes
startActivity(Intent(this, PdfDisplayActivity::class.java))

// 接收方
val pdfBytes = PdfDataHolder.pdfBytes ?: return
PdfDataHolder.pdfBytes = null // 使用后清空,防止内存泄漏

2. 传递给其他操作

保存到本地存储

fun savePdfToDocuments(context: Context, pdfBytes: ByteArray, fileName: String): Boolean {
    val docsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)
    val pdfFile = File(docsDir, "$fileName.pdf")
    return try {
        FileOutputStream(pdfFile).use { it.write(pdfBytes) }
        // 通知系统更新媒体库
        MediaScannerConnection.scanFile(context, arrayOf(pdfFile.path), arrayOf("application/pdf"), null)
        true
    } catch (e: Exception) {
        e.printStackTrace()
        false
    }
}

注意:Android 10+需适配Scoped Storage,Android 13+需申请READ_MEDIA_IMAGES/READ_MEDIA_DOCUMENTS权限。

分享PDF

fun sharePdf(context: Context, pdfBytes: ByteArray) {
    val tempFile = writePdfToTempCache(context, pdfBytes)
    val uri = FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", tempFile)
    val shareIntent = Intent(Intent.ACTION_SEND).apply {
        type = "application/pdf"
        putExtra(Intent.EXTRA_STREAM, uri)
        addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
    }
    context.startActivity(Intent.createChooser(shareIntent, "分享PDF"))
}

需在AndroidManifest.xml配置FileProvider:

<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

res/xml/file_paths.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <cache-path name="pdf_cache" path="." />
</paths>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 00:07:01