You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android WebView仅加载指定网站页面的URL拦截问题

解决Android WebView拦截外部链接失效的问题

嘿,我之前做WebView封装的时候也踩过这个一模一样的坑!明明重写了shouldOverrideUrlLoading,但广告和外链还是能跳出来,后来才发现是几个容易忽略的细节没处理好,给你梳理一下解决方案:

一、先排查核心问题:你可能只重写了单一版本的shouldOverrideUrlLoading

Android在API 24之后新增了带WebResourceRequest参数的shouldOverrideUrlLoading方法,如果你只重写了旧版本(接收String类型url的),那在Android 7.0+的设备上,新的跳转请求会走新版本的默认实现,导致你的拦截逻辑完全没生效!

必须同时重写两个版本的方法,把判断逻辑抽成公共方法,避免重复代码。

二、优化URL匹配逻辑:别用字符串前缀匹配,用Host判断更靠谱

你之前用前缀判断的方式很容易被绕过,比如广告链接伪装成https://yourdomain.com.evil.com,前缀匹配会误以为是自有链接,但实际Host是evil.com。正确的做法是解析URL的Host,判断是否属于你的域名:

示例代码:统一的URL拦截逻辑

webView.setWebViewClient(new WebViewClient() {
    // 适配API < 24
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        return isExternalUrl(url);
    }

    // 适配API >= 24
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
        String url = request.getUrl().toString();
        return isExternalUrl(url);
    }

    // 核心判断方法:检查URL是否为外部链接
    private boolean isExternalUrl(String url) {
        Uri uri = Uri.parse(url);
        String allowedHost = "yourdomain.com"; // 替换成你的自有域名

        // 处理大小写问题,统一转小写
        String actualHost = uri.getHost() != null ? uri.getHost().toLowerCase() : "";
        allowedHost = allowedHost.toLowerCase();

        // 判断是否为自有域名或子域名(如果需要允许子域名的话)
        boolean isAllowed = allowedHost.equals(actualHost) 
                || actualHost.endsWith("." + allowedHost);

        if (!isAllowed) {
            // 这里可以给用户提示,或者直接拦截
            Toast.makeText(getApplicationContext(), "此链接无法在App内打开", Toast.LENGTH_SHORT).show();
            return true; // 返回true表示拦截,不加载该链接
        }
        return false; // 返回false表示允许加载
    }
});

三、处理JavaScript跳转和新窗口请求

有些广告是通过JS跳转或者在新窗口打开的,这时候shouldOverrideUrlLoading可能不会触发,需要额外处理:

1. 拦截新窗口请求

重写WebChromeClientonCreateWindow方法,阻止新窗口创建:

webView.setWebChromeClient(new WebChromeClient() {
    @Override
    public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
        // 拦截所有新窗口请求
        Toast.makeText(getApplicationContext(), "禁止打开外部窗口", Toast.LENGTH_SHORT).show();
        return true; // 返回true表示我们自行处理,不创建新窗口
    }
});

2. 开启必要的WebSettings配置

确保WebView的JS支持开启(如果你的网站需要JS的话),同时禁用多窗口支持:

WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setSupportMultipleWindows(false); // 配合上面的新窗口拦截

四、额外防护:页面加载前再次校验

为了防止漏网之鱼,可以在onPageStarted方法里再次校验URL,如果是外部链接就停止加载并跳转到自有首页:

@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
    super.onPageStarted(view, url, favicon);
    if (isExternalUrl(url)) {
        view.stopLoading();
        // 跳转到自有网站首页
        view.loadUrl("https://yourdomain.com");
    }
}

最后总结一下你之前失效的原因

大概率是这两个问题:

  • 只重写了一个版本的shouldOverrideUrlLoading,导致高版本设备上拦截逻辑不生效
  • URL前缀匹配的方式有漏洞,被伪装的链接绕过了

按照上面的步骤修改后,应该就能彻底拦截所有外部链接,让WebView只加载你的自有网站内容啦!

内容的提问来源于stack exchange,提问作者ÊnnOâman

火山引擎 最新活动