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

Android WebView加载RecyclerView内PDF链接偶现内容不显示

问题根因与修复方案

核心Bug定位

  • 固定1秒延迟逻辑完全不可靠:Dialog View树完成挂载、WebView完成初始化与布局测量的耗时没有固定值,1秒的经验延迟无法适配不同性能的设备。当WebView未拿到真实宽高时加载页面,Google Docs预览页的JS会计算得到0尺寸的容器,直接渲染空白;当RecyclerView快速切换条目时,延迟任务还未执行,WebView已被回收,也会出现加载无响应。
  • PDF源URL未做编码:直接将原PDF地址拼接到Google Docs预览接口后,一旦原URL包含查询参数、特殊字符、中文,会直接截断预览接口的参数,导致实际拿到的PDF地址不完整,这是你遇到“部分场景正常、部分场景不显示”的核心诱因。
  • WebView基础配置缺失:当前仅开启JS与缩放权限,未开启DOM本地存储、缓存支持,而Google Docs预览页强依赖localStorage存储渲染状态,配置缺失会导致JS执行中断,出现白屏。
  • WebViewClient行为不一致:重载了两个版本的shouldOverrideUrlLoading方法,高版本方法直接调用super,其默认实现会拦截页面内部的跳转请求,导致Google Docs的加载流程中断,在不同系统版本上表现差异极大。
  • 加载时序不匹配:postDelayed的加载任务没有和WebView的窗口挂载状态绑定,当WebView还未显示到窗口时就触发加载,Google Docs的JS会检测到页面不可见,主动暂停渲染流程。

逐点修复实现

1. 替换固定延迟逻辑,绑定布局完成回调

删掉原有postDelayed(1000)逻辑,改用WebView布局回调判断容器真实尺寸,确保加载时机正确:

webView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
    @Override
    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
        // 仅当WebView拿到有效宽高时触发加载,仅执行一次
        if (right > 0 && bottom > 0) {
            webView.removeOnLayoutChangeListener(this);
            String pdfUrl = list.get(position).getURl();
            // 对原PDF URL做编码,避免特殊字符截断预览接口参数
            String encodedPdfUrl = Uri.encode(pdfUrl);
            webView.loadUrl("http://docs.google.com/gview?embedded=true&url=" + encodedPdfUrl);
        }
    }
});

2. 补全WebView必要配置

在原有设置基础上新增缺失项,解决JS执行中断问题:

WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setBuiltInZoomControls(true);
settings.setSupportZoom(true);
settings.setLoadWithOverviewMode(true);
settings.setUseWideViewPort(true);
// 新增必要配置
settings.setDomStorageEnabled(true);
settings.setCacheMode(WebSettings.LOAD_DEFAULT);
settings.setAllowContentAccess(true);
settings.setAllowFileAccess(true);

3. 统一WebViewClient行为,新增失败兜底

统一两个版本的URL拦截逻辑,避免内部请求被拦截,同时增加空页面校验与重试机制:

private class Callback extends WebViewClient {
    // 兼容低版本系统,不拦截任何跳转
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        return false;
    }

    // 兼容高版本系统,同样不拦截内部跳转,不要调用super
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
        return false;
    }

    @Override
    public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
        handler.proceed();
    }

    @Override
    public void onPageFinished(WebView view, String url) {
        super.onPageFinished(view, url);
        // 校验页面是否真实渲染完成,Google Docs加载完成后会生成canvas渲染层
        view.evaluateJavascript(
            "(function(){ return document.getElementsByTagName('canvas').length > 0 || document.body.innerText.trim().length > 0; })();",
            value -> {
                if (!Boolean.parseBoolean(value)) {
                    // 检测到空页面,触发重试
                    view.reload();
                }
            }
        );
    }

    @Override
    public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
        super.onReceivedError(view, request, error);
        // 主框架加载失败时自动重试
        if (request.isForMainFrame()) {
            view.reload();
        }
    }
}

4. 清理快速切换时的干扰任务

在Dialog关闭回调中主动释放WebView资源,避免旧的加载任务干扰新页面:

g.setOnDismissListener(dialog -> {
    webView.removeAllViews();
    webView.destroy();
});

注:不要再通过postDelayed加固定延迟掩盖时序问题,这类方式会因为设备性能差异、用户操作速度变化随时复现白屏,绑定真实的布局状态、渲染结果回调才是稳定实现的核心。

内容的提问来源于stack exchange,提问作者Ahmed Elsayed

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.22 19:54:06