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 request
。WebResourceRequest
這個東西是一個介面,並且是這樣定義的:
public interface WebResourceRequest {
Uri getUrl();
boolean isForMainFrame();
boolean hasGesture();
String getMethod();
Map<String, String> getRequestHeaders();
}
在其中沒有發現任何可以直接替換請求的方法。
然後搜尋了一下 Android 程式碼中對他的引用,private
static class WebResourceRequestImpl implements WebResourceRequest
它的內部實現僅僅是一個單純的實體。那這個東西要替換就非常好辦了,三個方法都可以做:
- 動態代理
- 反射
- 重新實現
實現
方案確定了,剩下的就簡單了。直接上程式碼。
首先是往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 的不同種類請求的時候可以用到。
-
更多敬請期待