当前位置: 首页 > news >正文

Android本地浏览PDF(Android PDF.js 简要学习手册)

环境

Min SDK: 21
依赖:

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1"
implementation "androidx.webkit:webkit:1.12.0"

权限:

<uses-permission android:name="android.permission.INTERNET" />

下载pdf.js :https://github.com/mozilla/pdf.js/tree/v2.10.377/web

引入步骤

app/src/main/assets/pdfjs/
├── web/
│   ├── viewer.html
│   ├── viewer.js
│   └── ...
├── build/
│   ├── pdf.js
│   ├── pdf.worker.js
│   └── ...

实现

import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.WindowManager
import android.webkit.WebChromeClient
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.core.content.ContextCompat
import androidx.webkit.WebViewAssetLoader
import com.gyf.immersionbar.ImmersionBar
import com.sunward.markettools.R
import com.sunward.markettools.base.BaseActivity
import com.sunward.markettools.databinding.ActivityWebviewBinding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.io.FileOutputStream
import java.net.URL
import java.net.URLEncoder
import java.nio.channels.Channels/***@dateTime:2025/4/29*@作者:MaoYoung*/
class WebViewPDFActivity : BaseActivity<MarketViewModel, ActivityWebviewBinding>(R.layout.activity_webview
) {companion object {fun newInstance(activity: Context?, url: String, fileName: String) {activity?.startActivity(Intent(activity, WebViewPDFActivity::class.java).apply {putExtra(activity.getString(R.string.params_web_url), url)putExtra(activity.getString(R.string.params_file_name), fileName)})}}private val coroutineScope = MainScope()private val domain = "appassets.androidplatform.net"override fun initView(savedInstanceState: Bundle?) {val pdfUrl =intent.getStringExtra(getString(R.string.params_web_url)) ?: ""val fileName = intent.getStringExtra(getString(R.string.params_file_name)) ?: ""window.setFlags(WindowManager.LayoutParams.FLAG_SECURE,WindowManager.LayoutParams.FLAG_SECURE)mBinding.apply {tvTitle.text = fileNametoolbar.setNavigationOnClickListener { finishActivity() }}val downloadDir = saveDownloadFilePath()val assetLoader = WebViewAssetLoader.Builder().setDomain(domain).addPathHandler("/assets/", WebViewAssetLoader.AssetsPathHandler(this)).addPathHandler("/Download/",WebViewAssetLoader.InternalStoragePathHandler(this, downloadDir)).build()mBinding.apply {webview.apply {settings.apply {javaScriptEnabled = truedomStorageEnabled = trueallowFileAccess = false // 不再需要 file 访问}//设置WebViewClient与AssetLoaderwebViewClient = object : WebViewClient() {override fun shouldInterceptRequest(view: WebView?,request: WebResourceRequest?): android.webkit.WebResourceResponse? {return assetLoader.shouldInterceptRequest(request?.url!!)}override fun shouldOverrideUrlLoading(view: WebView?,request: WebResourceRequest?): Boolean {return super.shouldOverrideUrlLoading(view, request)}override fun onPageFinished(view: WebView?, url: String?) {view?.loadUrl("javascript:(function() {" +"var rightToolbar = document.getElementById('toolbarViewerRight');" +"if (rightToolbar) { rightToolbar.style.display = 'none'; }" +"})()")}override fun onReceivedError(view: WebView?,errorCode: Int,description: String?,failingUrl: String?) {Log.e("WebViewActivity", "Error: $description, URL: $failingUrl")}}webChromeClient = WebChromeClient()downloadAndDisplayPdf(pdfUrl, fileName)}}}private fun saveDownloadFilePath(): File {val downloadDir = File(this.filesDir, "Download")if (!downloadDir.exists()) {downloadDir.mkdirs()}return downloadDir}private fun downloadAndDisplayPdf(pdfUrl: String, fileName: String) {coroutineScope.launch {try {val finalFileName = if (fileName.endsWith(".pdf")) fileName else "$fileName.pdf"val downloadedFileName =withContext(Dispatchers.IO) {val downloadDir = saveDownloadFilePath()val file = File(downloadDir, finalFileName)if (file.exists()) file.delete()val url = URL(pdfUrl)val connection = url.openConnection()connection.connect()val inputStream = connection.getInputStream()FileOutputStream(file).use { output ->Channels.newChannel(inputStream).use { inputChannel ->output.channel.use { outputChannel ->outputChannel.transferFrom(inputChannel, 0, Long.MAX_VALUE)}}}finalFileName}val encodedPath = URLEncoder.encode("$downloadedFileName", "UTF-8")val viewerUrl ="https://$domain/assets/pdfjs/web/viewer.html?file=%2FDownload%2F$encodedPath"mBinding.webview.loadUrl(viewerUrl)} catch (e: Exception) {e.printStackTrace()val encodedUrl = try {URLEncoder.encode(pdfUrl, "UTF-8")} catch (e: Exception) {pdfUrl}val fallbackUrl = "file:///android_asset/pdfjs/web/viewer.html?file=$encodedUrl"mBinding.webview.loadUrl(fallbackUrl)}}}override fun onResume() {super.onResume()mBinding.webview.onResume()}override fun onPause() {super.onPause()mBinding.webview.onPause()}override fun onDestroy() {mBinding.webview.destroy()coroutineScope.cancel()super.onDestroy()}override fun initImmersionBar() {ImmersionBar.with(this).statusBarColorTransformEnable(false).keyboardEnable(false).statusBarDarkFont(true).navigationBarDarkIcon(true).fitsSystemWindowsInt(true, ContextCompat.getColor(this, R.color.color_fa)).navigationBarColor(R.color.color_fa).init()}
}
WebViewActivity.newInstance(context, "这里传递下载PDF的地址")

工作原理

  1. 初始化

    • 从intent中获取PDF 的URL
    • 配置域名: .setDomain(domain)
  2. 下载
    使用协成下载到/Download/文件夹中

  3. 显示
    将文件加载到本地PDF路径(file://…/temp.pdf)的"https://$domain/assets/pdfjs/web/viewer.html

http://www.lryc.cn/news/591242.html

相关文章:

  • React hooks——useReducer
  • 面试Redis篇-深入理解Redis缓存穿透
  • 基于YOLOv11的水面垃圾智能检测系统
  • halcon 模板匹配
  • 高精度加法模版介绍
  • 阿里云-通义灵码:隐私保护机制—为数据安全筑起铜墙铁壁
  • USRP中心频率与采样率联合设置
  • MyBatis 之配置与映射核心要点解析
  • CPU架构、三级缓存以及内存优化屏障
  • 指针数组和数组指针的应用案例
  • 「Trae IDE 全流程实战」——从 0 下载安装,到在本地跑起一个可玩的 2048 小游戏
  • SpringBoot使用ThreadLocal共享数据
  • 永磁同步电机MTPA与MTPV曲线具体仿真实现
  • 大语言模型Gemini Deep Research 全流程解读+使用攻略
  • 杨耀东老师在ICML2025上对齐教程:《语言模型的对齐方法:一种机器学习视角》
  • 死信队列:springboot+RabbitMQ实现死信队列
  • GitHub Jekyll博客本地Win开发环境搭建
  • NumPy 数组存储字符串的方法
  • 算法提升之字符串练习-02(字符串哈希)
  • 【leetcode】852. 山脉数组的封顶索引
  • React 18 vs Vue3:状态管理方案深度对比
  • 深入理解Map.Entry.comparingByValue()和Map.Entry.comparingByKey()
  • 我爱学算法之—— 前缀和(下)
  • 第十四章 gin基础
  • 深入理解React Hooks:从使用到原理
  • Qt CMake 学习文档
  • 【安卓按键精灵辅助工具】adb调试工具连接安卓模拟器异常处理
  • QT之openGL使用(二)
  • 端到端神经网络视频编解码器介绍
  • 电脑截图软件排行榜 Windows和mac电脑截图软件TOP10