如何高效使用WebView的evaluateJavaScript方法传输文件?
优化Android WebView加载PDF的方案
你的核心问题在于通过evaluateJavascript传递完整字节数组——这种方式会将每个字节序列化为JSON元素,不仅内存开销极大,还会阻塞跨进程通信,直接导致大文件加载失败。下面是两种更优的解决方案:
方案一:直接启用WebView原生文件输入功能
让WebView自带的文件输入控件处理选择逻辑,完全跳过Android层的字节数组中转,是最简单高效的方案。
步骤1:配置WebView允许文件访问
在初始化WebView时添加以下设置:
val webSettings = myWebView.settings webSettings.javaScriptEnabled = true webSettings.allowFileAccess = true webSettings.allowContentAccess = true webSettings.domStorageEnabled = true
步骤2:前端使用原生文件输入控件
在你的HTML页面中添加标准文件选择按钮:
<input type="file" accept="application/pdf" id="pdfInput"> <script> document.getElementById('pdfInput').addEventListener('change', (e) => { const file = e.target.files[0]; if (!file) return; const fileReader = new FileReader(); fileReader.onload = function() { renderPdf(new Uint8Array(this.result)); }; fileReader.readAsArrayBuffer(file); }); </script>
这种方式下,用户点击文件选择按钮后,WebView会自动调用系统文件选择器,选中文件后JS直接获取File对象,无需Android层介入,完全避免字节数组传递的性能问题。
方案二:传递Content URI给JS直接读取
如果需要在Android层先对文件做预处理(比如验证、日志),可以传递文件的Content URI给JS,让JS通过网络请求方式读取文件内容,而非传递完整字节数组。
步骤1:修改Android代码传递URI并授予权限
修改你的resultLauncher逻辑,不再读取字节数组,而是传递URI并授予WebView持久访问权限:
private var resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> if (result.resultCode == RESULT_OK) { result?.data?.also { intent -> val uri = intent.data ?: return@also // 授予WebView持久读取权限 contentResolver.takePersistableUriPermission( uri, Intent.FLAG_GRANT_READ_URI_PERMISSION ) // 传递URI给JS myWebView.evaluateJavascript("renderPdfByUri('${uri.toString()}')", null) } } }
步骤2:JS通过URI读取文件内容
修改JS方法,通过fetch读取URI对应的文件:
function renderPdfByUri(uri) { fetch(uri) .then(response => response.arrayBuffer()) .then(buffer => { renderPdf(new Uint8Array(buffer)); }) .catch(error => console.error('加载PDF失败:', error)); }
这种方式仅传递一个字符串URI,内存开销可以忽略,且支持任意大小的PDF文件,同时保留了Android层对文件选择的控制能力。
方案对比
- 方案一:无需修改Android层业务逻辑,完全依赖WebView原生能力,开发成本最低,适合无特殊预处理需求的场景。
- 方案二:保留Android层对文件的控制权,适合需要先验证文件、记录日志等前置操作的场景,性能同样优异。
内容的提问来源于stack exchange,提问作者Eboy
相关产品推荐
相关产品推荐

