Android中如何攔截WebView的請求

Android中如何攔截WebView的請求

文章來自cnzx219在Github上的分享,這裏先對他表示感謝。附地址:https://github.com/cnzx219/my-note/blob/master/2015/android-webview-intercept-request.mdandroid

一、需求背景

接到這樣一個需求,須要在 WebView 的全部網絡請求中,在請求的url中,加上一個xxx=1的標誌位。
例如 http://www.baidu.com 加上標誌位就變成了 http://www.baidu.com?xxx=1git

二、尋找解決方案

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

public WebResourceResponse shouldInterceptRequest(WebView view, String url)

就是說只要實現 WebViewClient 的 shouldInterceptRequest 方法,而後調用 WebView 的 setWebViewClient 就能夠了。web

可是,在 API21 以上又棄用了上述 API,使用了一條新的 API,以下:網絡

public WebResourceResponse shouldInterceptRequest(WebView view, final WebResourceRequest request)

好吧,爲了支持儘可能多的版本,看來兩個都須要實現了,發現一看就很是好用的 String url 變成了一個 WebResourceRequest request。WebResourceRequest 這個東西是一個接口,而且是這樣定義的:ide

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

在其中沒有發現任何能夠直接替換請求的方法。url

而後搜索了一下 Android 代碼中對他的引用,點我搜索。而後發現 private static class WebResourceRequestImpl implements WebResourceRequest 它的內部實現僅僅是一個單純的實體。那這個東西要替換就很是好辦了,三個方法均可以作:代理

  1. 動態代理code

  2. 反射orm

  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, WebResourceRequest request) {

    if (request != null && request.getUrl() != null && request.getMethod().equalsIgnoreCase("get")) {
        String scheme = request.getUrl().getScheme().trim();
        if (scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https")) {
            try {
                URL url = new URL(injectIsParams(request.getUrl().toString()));
                URLConnection connection = url.openConnection();
                return new WebResourceResponse(connection.getContentType(), connection.getHeaderField("encoding"), connection.getInputStream());
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    return null;

    }

@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")) {
            try {
                URL url = new URL(injectIsParams(request.getUrl().toString()));
                URLConnection connection = url.openConnection();
                return new WebResourceResponse(connection.getContentType(), connection.getHeaderField("encoding"), connection.getInputStream());
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    return null;
}
});

大功告成。

注意: 注意保護 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 的不一樣種類請求的時候能夠用到。

更多敬請期待...

相關文章
相關標籤/搜索