shouldInterceptRequest 是WebViewClient的一个方法,官方的说明:
* Notify the host application of a resource request and allow the
* application to return the data. If the return value is null, the WebView
* will continue to load the resource as usual. Otherwise, the return
* response and data will be used. NOTE: This method is called on a thread
* other than the UI thread so clients should exercise caution
* when accessing private data or the view system.
* @param view The {@link android.webkit.WebView} that is requesting the
* resource.
* @param request Object containing the details of the request.
* @return A {@link android.webkit.WebResourceResponse} containing the
* response information or null if the WebView should load the
* resource itself.
public WebResourceResponse shouldInterceptRequest(WebView view,
WebResourceRequest request) {
return shouldInterceptRequest(view, request.getUrl().toString());
大致翻译:
当webview页面有资源请求的时候通知宿主应用,允许应用自己返回数据给webview。如果返回值是null,就正常加载返回的数据,否则就加载应用自己return的response给webview。注意,这个方法回调在子线程而不是UI线程所以在操作私有数据或者view视图的时候要小心。
通常这个方法是用来监控所有的页面请求的,可以用它来监控黑名单以防止页面劫持,当怀疑域名被劫持时,可以通过本地http请求代理,然后将结果返回给webview。下面是我的代码的简化
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
try {
//走本地代理
InputStream is;
if(isHocked) {
ResponseBody httpResponse = HttpProxyClient.execute(request);
is = httpResponse.byteStream();
retrun new WebResourceResponse(httpResponse.contentType().type() + "/" + httpResponse.contentType().subtype(),
responseBody.contentType().charset().displayName(), is)
} catch (Exception e) {
e.printStackTrace();
} finally {
if(is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
return null;
然而当我走到这段代码,网页无一例外,都显示了出错,走到了错误占位页。方的不行,以为是网络请求出了问题,debug跟到httpResponseBody,发现response code为200。但是再往下走,返回了WebResourceResponse之后,仍然走到了错误页。我百思不得其解。打了一堆日志也没有发现有什么问题。
折腾了一天,一筹莫展。偶然的我把日志换成Verbose并且去掉所有过滤,看到了这么一行:
E/InputStreamUtil: Got exception when calling available() on an InputStream returned from shouldInterceptRequest. This will cause the related request to fail.
说是调用从shouldInterceptRequest返回的inputStream 调用available()发生异常,会导致相关的请求失败。看了下这个方法的说明:
* Returns an estimate of the number of bytes that can be read (or
* skipped over) from this input stream without blocking by the next
* invocation of a method for this input stream. The next invocation
* might be the same thread or another thread. A single read or skip of this
* many bytes will not block, but may read or skip fewer bytes.
* <p> Note that while some implementations of {@code InputStream} will return
* the total number of bytes in the stream, many will not. It is
* never correct to use the return value of this method to allocate
* a buffer intended to hold all data in this stream.
* <p> A subclass' implementation of this method may choose to throw an
* {@link IOException} if this input stream has been closed by
* invoking the {@link #close()} method.
* <p> The {@code available} method for class {@code InputStream} always
* returns {@code 0}.
* <p> This method should be overridden by subclasses.
* @return an estimate of the number of bytes that can be read (or skipped
* over) from this input stream without blocking or {@code 0} when
* it reaches the end of the input stream.
* @exception IOException if an I/O error occurs.
public int available() throws IOException {
return 0;
这个方法是被子类重写的,官方的建议是如果inputstream的close被调用,子类重写时可以在里面抛IO异常。到这里就有些眉目了,有可能是httpResponse.byteStream()返回的inputStream类型,它的available()实现是如果调用了close就抛出异常。而我的代码里在finally中确实调用了close导致的。那么httpResponse.byteStream()返回的inputStream类型是不是真的就是这么实现的呢?byteStream()返回的是BufferSource的inputStream(),
我用的是Okhttp3,跟踪代码发现它使用的是RealBufferSource,继承了BufferSource。它的inputStream 方法的实现:
public InputStream inputStream() {
return new InputStream() {
public int read() throws IOException {
if(RealBufferedSource.this.closed) {
throw new IOException("closed");
} else {
if(RealBufferedSource.this.buffer.size == 0L) {
long count = RealBufferedSource.this.source.read(RealBufferedSource.this.buffer, 8192L);
if(count == -1L) {
return -1;
return RealBufferedSource.this.buffer.readByte() & 255;
public int read(byte[] data, int offset, int byteCount) throws IOException {
if(RealBufferedSource.this.closed) {
throw new IOException("closed");
} else {
Util.checkOffsetAndCount((long)data.length, (long)offset, (long)byteCount);
if(RealBufferedSource.this.buffer.size == 0L) {
long count = RealBufferedSource.this.source.read(RealBufferedSource.this.buffer, 8192L);
if(count == -1L) {
return -1;
return RealBufferedSource.this.buffer.read(data, offset, byteCount);
public int available() throws IOException {
if(RealBufferedSource.this.closed) {
throw new IOException("closed");
} else {
return (int)Math.min(RealBufferedSource.this.buffer.size, 2147483647L);
public void close() throws IOException {
RealBufferedSource.this.close();
public String toString() {
return RealBufferedSource.this + ".inputStream()";
可以看到确实available()里面如果调用过close,会抛出IO异常。到此,这个谜题就都解开了。
我把finally中的close操作去掉,果然就能正常打开页面了。
这个问题查了好久没找到原因,是因为对这个机制不熟悉,理解不够充分。从写法上看,在finally里面关闭流是最正常不过的事情了,但是关了就会导致所有走代理的请求web view全都不认。仔细想想,为什么WebResourceResponse的构造入参要有这个么个东西?因为app代理的response要交给webview处理,webview要拿到response里的内容,要么直接丢给它一个Response类型,但是不同的http工具定义的response不是一个东西,webview应该对客户端自己用什么http方式不关心的,所以它只拿了一个inputstream的流。
shouldInterceptRequest 是WebViewClient的一个方法,官方的说明:/** * Notify the host application of a resource request and allow the * application to return the data. If the return value is null, the We...
webp格式图片
webp格式图片是google推出的,相比jpg png有着巨大的优势,同样质量的图片webp格式的图片占用空间更小,在像电商这样图片比较多的App中,使用webp格式图片会很有优势。
很早之前,我们的项目中就已经采用了webp格式,但是由于webView本身并不能解析webp格式,所以我们基于webView的文章详情页就无法使用到这项优化。
那么有没有什么办法能实现呢?当然是有的。
在开始技术讲解之前需要先说明,本文的技术方案,是基于本项目的情况:文章的正文大部分通过接口直接获取到,通过在客户端本地进行html正文组装,最后通过webView的loadHTMLSt
webView.setWebViewClient(new WebViewClient() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
try {
URL url =
WebView 的 shouldInterceptRequest 可以让我们监听WebView发出的请求并做相应的处理,但有个严重的问题:浏览器的渲染以及资源加载都是在一个线程中,如果在shouldInterceptRequest 处理时间过长,WebView界面就会阻塞,这是非常非常不好的。
本人最近遇到个问题,用WebView做显示,里面有个别特定url的图片需要做特殊的加载处理
接到这样一个需求,需要在 WebView 的所有网络请求中,在请求的url中,加上一个sign=xxxx 的标志位,同时添加手机本地的数据比如 sessionToken=sd54f5sd4ffsdf45454564
、deviceId=863970025919887
例如 http://www.baidu.com 加上标志位就变成了 http://www.baidu.c
WebView 的 shouldInterceptRequest 可以让我们监听WebView发出的请求并做相应的处理,但有个严重的问题:浏览器的渲染以及资源加载都是在一个线程中,如果在shouldInterceptRequest 处理时间过长,WebView界面就会阻塞,这是非常非常不好的。
本人最近遇到个问题,用WebView做显示,里面有个别特定url的图片需要做特殊的加载处理。所...
在资源检查中,可以通过覆盖shouldOverrideUrlLoading和shouldInterceptRequest方法来检查对内容和/或资源的请求。
但上述两种方法用于不同的目的,例如
1.当新页面即将被打开时调用shouldOverrideUrlLoading,而每次资源被加载的时候都会调用shouldI
HttpServletRequest:
HttpServletRequest对象代表用户端浏览器发来的请求,当用户通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,通过这个对象提供的方法,可以获得客户端请求的所有信息。