文章來自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=1。git
從 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 它的內部實現僅僅是一個單純的實體。那這個東西要替換就很是好辦了,三個方法均可以作:代理
動態代理code
反射orm
從新實現
方案肯定了,剩下的就簡單了。直接上代碼。
首先是往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 的不一樣種類請求的時候能夠用到。
更多敬請期待...