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

Android WebView如何拦截并修改WebSocket请求头?

问题

在Android WebView中发起WebSocket请求时,需要在请求发送前修改其请求头。尝试使用自定义WebViewClient的shouldInterceptRequest方法,但该方法仅能拦截HTTP请求,无法拦截WebSocket请求——通过request.getUrl().toString()记录请求URL,发现WebSocket请求均未被拦截。

现需解决:

  1. Android WebView中是否存在拦截并修改WebSocket请求头的方法?
  2. 若不存在,是否有替代框架或方案可实现该需求?

注:曾在Electron JS中通过session.defaultSession.webRequest.onBeforeSendHeaders成功修改WebSocket请求头,需适配Android的类似解决方案。

相关代码:

class MainActivity extends AppCompatActivity {

    private WebView webView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        webView = findViewById(R.id.webview);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.setWebViewClient(new MyWebViewClient());
        webView.loadUrl("https://panel.autotcrypto.com");
    }

    private class MyWebViewClient extends WebViewClient {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
            return false;
        }

        @Override
        public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
            String url = request.getUrl().toString();
            if (url.contains("ws")) {
                // 尝试修改请求头的代码(对WebSocket无效)
            }
            return super.shouldInterceptRequest(view, request);
        }
    }
}
解决方案

一、原生WebView的限制说明

Android系统原生WebView的shouldInterceptRequest方法仅针对HTTP/HTTPS请求生效,WebSocket属于独立的协议通道,不走HTTP拦截流程,因此原生WebView没有直接拦截并修改WebSocket请求头的API。

二、可行替代方案

方案1:JS注入替换WebSocket(适用于页面可控场景)

如果WebView加载的页面是你方可控的,可以通过注入JavaScript重写页面的WebSocket构造函数,在握手阶段添加允许的自定义请求头(浏览器对WebSocket自定义头有严格限制,仅支持部分标准头或Sec-WebSocket-*前缀的头)。

步骤示例:

  1. 在WebView页面加载完成后注入JS:
webView.setWebViewClient(new WebViewClient() {
    @Override
    public void onPageFinished(WebView view, String url) {
        super.onPageFinished(view, url);
        // 注入重写WebSocket的JS代码
        view.evaluateJavascript(
            "(function() {" +
                "const OriginalWebSocket = window.WebSocket;" +
                "window.WebSocket = function(url, protocols) {" +
                    // 构造时可尝试添加自定义头(需符合浏览器规则)
                    "const ws = new OriginalWebSocket(url, protocols);" +
                    // 若需传递自定义信息,也可通过URL参数间接实现(服务端配合解析)
                    "return ws;" +
                "};" +
            "})();",
            null
        );
    }
});

注意:浏览器禁止WebSocket握手请求携带任意自定义头,仅允许如Authorization这类部分标准头,或与WebSocket相关的Sec-WebSocket-*头,具体需参考浏览器规范。

方案2:Native接管WebSocket请求(完全可控)

通过OkHttp实现自定义WebSocket客户端,配合WebView的JS交互,完全接管页面的WebSocket请求,实现任意修改请求头的需求。

核心思路:

  • 注入JS替换页面的WebSocket对象,让页面通过JS调用Native的WebSocket客户端;
  • Native用OkHttp创建WebSocket连接,在请求拦截器中修改请求头;
  • 建立连接后,将WebSocket的消息、状态变化通过JS回调同步给页面。

代码示例:

public class MainActivity extends AppCompatActivity {
    private WebView webView;
    private WebSocket webSocket;
    private OkHttpClient okHttpClient;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        webView = findViewById(R.id.webview);
        WebSettings settings = webView.getSettings();
        settings.setJavaScriptEnabled(true);
        // 添加JS交互接口
        webView.addJavascriptInterface(new WebSocketJsBridge(), "androidWebSocket");

        // 初始化OkHttpClient,添加请求头拦截器
        okHttpClient = new OkHttpClient.Builder()
                .addInterceptor(chain -> {
                    Request originalRequest = chain.request();
                    // 修改WebSocket握手请求头
                    Request modifiedRequest = originalRequest.newBuilder()
                            .addHeader("Custom-Header", "custom-value")
                            .addHeader("Authorization", "Bearer your-token")
                            .build();
                    return chain.proceed(modifiedRequest);
                })
                .build();

        webView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageFinished(WebView view, String url) {
                super.onPageFinished(view, url);
                // 注入替换WebSocket的JS代码
                injectWebSocketOverrideJS();
            }
        });
        webView.loadUrl("https://panel.autotcrypto.com");
    }

    private void injectWebSocketOverrideJS() {
        String jsCode = "(function() {" +
                "window.WebSocket = function(url, protocols) {" +
                    "const wsInstance = {" +
                        "onopen: null," +
                        "onmessage: null," +
                        "onerror: null," +
                        "onclose: null," +
                        "send: function(msg) {" +
                            "androidWebSocket.send(msg);" +
                        "}," +
                        "close: function(code, reason) {" +
                            "androidWebSocket.close(code || 1000, reason || 'Normal close');" +
                        "}" +
                    "};" +
                    "window.WebSocket.currentInstance = wsInstance;" +
                    "androidWebSocket.connect(url, protocols || '');" +
                    "return wsInstance;" +
                "};" +
            "})();";
        webView.evaluateJavascript(jsCode, null);
    }

    // JS交互桥接类
    private class WebSocketJsBridge {
        @JavascriptInterface
        public void connect(String url, String protocols) {
            // 创建WebSocket请求
            Request request = new Request.Builder().url(url).build();
            webSocket = okHttpClient.newWebSocket(request, new WebSocketListener() {
                @Override
                public void onOpen(WebSocket webSocket, Response response) {
                    // 通知页面连接成功
                    webView.post(() -> {
                        webView.evaluateJavascript("if(window.WebSocket.currentInstance && window.WebSocket.currentInstance.onopen) window.WebSocket.currentInstance.onopen();", null);
                    });
                }

                @Override
                public void onMessage(WebSocket webSocket, String text) {
                    // 通知页面收到消息
                    webView.post(() -> {
                        String jsCall = String.format("if(window.WebSocket.currentInstance && window.WebSocket.currentInstance.onmessage) window.WebSocket.currentInstance.onmessage({data: '%s'});", text.replace("'", "\\'"));
                        webView.evaluateJavascript(jsCall, null);
                    });
                }

                @Override
                public void onFailure(WebSocket webSocket, Throwable t, Response response) {
                    // 通知页面连接失败
                    webView.post(() -> {
                        webView.evaluateJavascript("if(window.WebSocket.currentInstance && window.WebSocket.currentInstance.onerror) window.WebSocket.currentInstance.onerror();", null);
                    });
                }

                @Override
                public void onClosing(WebSocket webSocket, int code, String reason) {
                    webSocket.close(code, reason);
                }

                @Override
                public void onClosed(WebSocket webSocket, int code, String reason) {
                    // 通知页面连接关闭
                    webView.post(() -> {
                        String jsCall = String.format("if(window.WebSocket.currentInstance && window.WebSocket.currentInstance.onclose) window.WebSocket.currentInstance.onclose({code: %d, reason: '%s'});", code, reason.replace("'", "\\'"));
                        webView.evaluateJavascript(jsCall, null);
                    });
                }
            });
        }

        @JavascriptInterface
        public void send(String message) {
            if (webSocket != null) {
                webSocket.send(message);
            }
        }

        @JavascriptInterface
        public void close(int code, String reason) {
            if (webSocket != null) {
                webSocket.close(code, reason);
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (webSocket != null) {
            webSocket.close(1000, "Activity destroyed");
        }
    }
}

该方案完全可控,可任意修改WebSocket握手请求头,缺点是需要适配页面的WebSocket调用逻辑,处理所有事件回调,实现成本较高。

方案3:第三方WebView框架(不推荐)

曾经的CrossWalk WebView支持拦截WebSocket请求,但目前CrossWalk已停止维护,存在兼容性和安全风险,不建议使用。

总结

  • 原生WebView无直接拦截修改WebSocket请求头的方法;
  • 页面可控时优先选择JS注入方案(注意浏览器头限制);
  • 需完全控制请求头时,采用OkHttp+JS交互的Native接管方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 08:04:54