1. 程式人生 > >Android中WebView中攔截所有請求並替換URL

Android中WebView中攔截所有請求並替換URL

我的部落格原文地址:

http://hiroz.cn/android-webview-intercept-request/

需求背景

接到這樣一個需求,需要在 WebView 的所有網路請求中,在請求的url中,加上一個xxx=1的標誌位。

例如 http://www.baidu.com 加上標誌位就變成了 http://www.baidu.com?xxx=1

尋找解決方案

從 Android API 11 (3.0) 開始,WebView 開始在 WebViewClient 內提供了這樣一條 API ,如下:

public WebResourceResponse shouldInterceptRequest
(WebView view, String url)

就是說只要實現 WebViewClient 的 shouldInterceptRequest 方法,然後呼叫 WebView 的setWebViewClient 就可以了。

但是,在 API21 以上又棄用了上述 API,使用了一條新的 API,如下:

public WebResourceResponse shouldInterceptRequest(WebView view, final WebResourceRequest request)  

好吧,為了支援儘量多的版本,看來兩個都需要實現了,發現一看就非常好用的 String url

 變成了一個WebResourceRequest requestWebResourceRequest 這個東西是一個介面,並且是這樣定義的:

public interface WebResourceRequest {  
    Uri getUrl();
    boolean isForMainFrame();
    boolean hasGesture();
    String getMethod();
    Map<String, String> getRequestHeaders();
}

在其中沒有發現任何可以直接替換請求的方法。

然後搜尋了一下 Android 程式碼中對他的引用,

點我搜索。然後發現 private static class WebResourceRequestImpl implements WebResourceRequest 它的內部實現僅僅是一個單純的實體。那這個東西要替換就非常好辦了,三個方法都可以做:

  1. 動態代理
  2. 反射
  3. 重新實現

實現

方案確定了,剩下的就簡單了。直接上程式碼。

首先是往URL字串加那個標誌位的方法

public static String injectIsParams(String url) {  
    if (url != null && !url.contains("xxx=") {
        if (url.contains("?")) {
            return url + "&xxx=1";
        } else {
            return url + "?xxx=1";
        }
    } else {
        return url;
    }
}

然後要攔截所有請求了

webView.setWebViewClient(new WebViewClient() {

    @SuppressLint("NewApi")
    @Override
    public WebResourceResponse shouldInterceptRequest(WebView view, final WebResourceRequest request) {
        if (request != null && request.getUrl() != null) {
            String scheme = request.getUrl().getScheme().trim();
            if (scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https")) {
                return super.shouldInterceptRequest(view, new WebResourceRequest() {
                    @Override
                    public Uri getUrl() {
                        return Uri.parse(injectIsParams(request.getUrl().toString()));
                    }

                    @SuppressLint("NewApi")
                    @Override
                    public boolean isForMainFrame() {
                        return request.isForMainFrame();
                    }

                    @SuppressLint("NewApi")
                    @Override
                    public boolean hasGesture() {
                        return request.hasGesture();
                    }

                    @SuppressLint("NewApi")
                    @Override
                    public String getMethod() {
                        return request.getMethod();
                    }

                    @SuppressLint("NewApi")
                    @Override
                    public Map<String, String> getRequestHeaders() {
                        return request.getRequestHeaders();
                    }
                });
            }
        }
        return super.shouldInterceptRequest(view, request);
    }


    @Override
    public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
        if (!TextUtils.isEmpty(url) && Uri.parse(url).getScheme() != null) {
            String scheme = Uri.parse(url).getScheme().trim();
            if (scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https")) {
                return super.shouldInterceptRequest(view, injectIsParams(url));
            }
        }
        return super.shouldInterceptRequest(view, url);
    }

});

大功告成。

歡迎指出程式碼中的問題~~一起學習進步

注意: 注意保護 URL 的 Scheme,在程式碼中特地過濾了 http 和 https。

引申

上邊的 API 中發現還能有更多的玩法,比如:

  • 替換 WebResourceResponse,構造一個自己的 WebResourceResponse。比如下列程式碼,用一個包裡的本地檔案替換掉要請求的網路圖片。
WebResourceResponse response = null;  
if (url.contains("logo")) {  
    try {
        InputStream is = getAssets().open("test.png");
        response = new WebResourceResponse("image/png", "UTF-8", is);
    } catch (IOException e) {
        e.printStackTrace();
    }        
}
return response;  
  • 在 API 21 (5.0) 以上的版本使用了 WebResourceRequest 介面,這個介面能修改發出請求的 Header
@Override
public Map<String, String> getRequestHeaders() {  
    return request.getRequestHeaders();
}
  • 在 API 21 (5.0) 以上的版本中可以區分 GET 請求和 POST 請求,在某些情況下,需要區分 AJAX 的不同種類請求的時候可以用到。

  • 更多敬請期待