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

Android WebView预加载后延迟显示AlertDialog异常求助

解决延迟调用WebView弹窗空白且JS接口不触发的问题

这种场景我之前做类似功能的时候也踩过坑,分开调用fetchPopUp和displayPopUp时WebView的状态很容易因为生命周期和附着问题出异常,咱们一步步来拆解和解决:

核心问题分析

连续调用时功能正常,延迟调用就失效,主要有两个关键原因:

  1. WebView生命周期脱离:当你调用fetchPopUp加载完页面后,WebView还没有被添加到任何窗口容器(比如AlertDialog)中,系统内核会认为这个WebView是“无用”的,可能会回收它的页面资源、暂停JS执行,甚至销毁页面上下文,导致后来展示时空白。
  2. JS接口绑定时机太晚:你原来在displayPopUp里才添加JavascriptInterface,但如果是延迟调用,这时候WebView的页面已经加载完成了,页面中的JS代码早就执行过,根本找不到WAIAndroid这个接口,自然不会触发resize方法。

针对性解决方案

1. 提前绑定JS接口+页面加载完成后触发resize

把JS接口绑定移到fetchPopUp中,并且在onPageFinished回调里触发resize,确保页面DOM完全就绪时再调用JS方法:

public void fetchPopUp(String url, Context appContext, Activity activity) {
    webView = new WebView(appContext);
    WebSettings settings = webView.getSettings();
    settings.setJavaScriptEnabled(true);
    // 关键:提前绑定JS接口,让页面加载时就能访问到
    webView.addJavascriptInterface(new TCWebAppInterface(null, webView, activity), "WAIAndroid");
    // 开启DOM存储,避免页面状态丢失
    settings.setDomStorageEnabled(true);

    webView.setWebViewClient(new WebViewClient() {
        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            // 页面加载完成后再触发resize,确保DOM已就绪
            view.loadUrl("javascript:WAIAndroid.resize(document.body.getBoundingClientRect().height)");
        }
    });
    webView.loadUrl(url);
}

2. 给WebView临时添加父容器,维持生命周期

延迟调用时,WebView加载完页面后没有附着到窗口,很容易被系统回收。可以把它临时添加到Activity的DecorView中(不可见状态),保持页面活跃:

public void fetchPopUp(String url, Context appContext, Activity activity) {
    webView = new WebView(appContext);
    WebSettings settings = webView.getSettings();
    settings.setJavaScriptEnabled(true);
    settings.setDomStorageEnabled(true);
    webView.addJavascriptInterface(new TCWebAppInterface(null, webView, activity), "WAIAndroid");

    // 临时添加到Activity的DecorView,维持WebView生命周期
    ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
    // 设置0尺寸,确保不会显示出来
    webView.setLayoutParams(new ViewGroup.LayoutParams(0, 0));
    decorView.addView(webView);

    webView.setWebViewClient(new WebViewClient() {
        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            view.loadUrl("javascript:WAIAndroid.resize(document.body.getBoundingClientRect().height)");
        }
    });
    webView.loadUrl(url);
}

public void displayPopUp(Activity activity) {
    // 先从临时容器移除WebView
    ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
    decorView.removeView(webView);

    // 创建弹窗并添加WebView
    AlertDialog.Builder builder;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        builder = new AlertDialog.Builder(activity, android.R.style.Theme_Material_Dialog_Alert);
    } else {
        builder = new AlertDialog.Builder(activity);
    }
    builder.setView(webView);
    AlertDialog dial = builder.show();
    
    // 如果你需要在TCWebAppInterface里操作Dialog,可以在这里更新引用
    // 比如给TCWebAppInterface加个setDialog方法:
    // ((TCWebAppInterface)webView.getJavascriptInterface("WAIAndroid")).setDialog(dial);
}

3. 优化TCWebAppInterface的Dialog引用

原来的代码里在displayPopUp才传入Dialog,但延迟调用时WebView已经加载完,这时候再传引用可能无效。可以给TCWebAppInterface添加一个setDialog方法,在displayPopUp时更新:

public class TCWebAppInterface {
    private AlertDialog dial;
    private WebView webView;
    private Activity activity;

    public TCWebAppInterface(AlertDialog dial, WebView webView, Activity activity) {
        this.dial = dial;
        this.webView = webView;
        this.activity = activity;
    }

    // 添加更新Dialog的方法
    public void setDialog(AlertDialog dial) {
        this.dial = dial;
    }

    @JavascriptInterface
    public void resize(final float height) {
        activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Log.e("JIBA", "sizes\n" + activity.getResources().getDisplayMetrics().widthPixels + "\n" + height + "\n" + activity.getResources().getDisplayMetrics().density + "\n" + activity.getResources().getDisplayMetrics().heightPixels);
                // 如果需要调整Dialog尺寸,这里可以用更新后的dial引用
            }
        });
    }
}

为什么这样能解决问题?

  • 提前绑定JS接口,确保页面加载时就能找到WAIAndroid,不会错过JS的执行时机;
  • 临时添加父容器,让WebView始终处于活跃状态,避免系统回收页面资源;
  • 在onPageFinished触发resize,保证DOM完全渲染完成后再获取高度,结果更准确。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:35:44