拦截并覆盖WebView的HTTP请求

74

我在应用程序中有一个 WebView,在其中打开了一些网站(始终是同一个,是我的自己的网页)。该网站具有加载来自远程主机的某些图像的一些 JS 代码。

我希望拦截对这些图像的请求(通过 URL 模式),并返回自己的内容(即另一张图像),或根据内部应用程序逻辑保持请求不变。

是否可能做到这一点?

编辑:问题的当前状态...

WebView 有设置 WebViewClient 的能力(如 Axarydax 所指出)。WebViewClient 有两个有用的方法:

  • shouldOverrideUrlLoading
  • onLoadResource

shouldOverrideUrlLoading 能够拦截任何 URL 加载,如果加载是由页面交互触发的(例如点击页面上的链接,WebView.loadURL("") 不触发此方法)。它还能通过返回 false 来取消 URL 加载。但是,这种方法无法拦截页面资源的加载(而图片是我需要拦截的这种页面资源)。

onLoadResource 触发每次页面资源(包括图片!感谢 Jessyjones)加载时,但无法取消。这使得这种方法也不适合我的任务。

7个回答

79

试试这个,我在一个类似个人wiki的应用中使用过:

webView.setWebViewClient(new WebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if (url.startsWith("foo://")) {
            // magic
            return true;
        }
        return false;
    }
});

2
你可以使用WebView的loadData()方法将一些HTML代码推送到其中。用法:webView.loadData("<html>...</html>", "text/html", "utf-8"); - Axarydax
不太合适。通过loadData加载的静态内容没有访问额外通过网络加载的其他内容的权限。作为解决方法,可以使用loadData("javascript:....")与一些"客户端"代码进行交互。如果shouldOverrideUrlLoading针对每个页面资源(例如内容图片)都被调用,那么这可能是一个好的和适当的方法。但是它并没有。 - Olegas
The method ShouldOverrideUrlLoading(WebView, String) of type new WebViewClient(){} must override or implement a supertype method - Francisco Corrales Morales
那么检查呢?(https://dev59.com/smAg5IYBdhLWcg3wE3nQ) - Francisco Corrales Morales
3
自 API 24 开始,shouldOverrideUrlLoading 的这个版本已经被弃用。要解决这个问题,请参考这个问题:https://dev59.com/h1oV5IYBdhLWcg3wPs4Y - Jonik
显示剩余3条评论

55

11
注意!如果你拦截了浏览器请求图片(或其他资源)并返回null(表示让WebView继续获取图片),则将来对该资源的任何请求可能都会直接从缓存获取,而不会触发shouldInterceptRequest()。如果你想拦截每个图片请求,你需要禁用缓存或者(像我所做的)调用WebView.clearCache(true) - ADDruid
2
我正在尝试添加自定义的cookies和额外的headers。有任何想法如何实现吗? - narancs
自API 24版本开始,接受String url参数的shouldInterceptRequest方法已被弃用。为了解决这个问题,请参考此问题:https://dev59.com/h1oV5IYBdhLWcg3wPs4Y(关于弃用,`shouldInterceptRequest`类似于`shouldOverrideUrlLoading`)。 - Jonik
这适用于问题中的特定情况,但并不适用于所有网络调用,因为shouldInterceptRequest()方法不提供POST请求的数据。 - Miloš Černilovský

12
据我所知,shouldOverrideUrlLoading 方法不是用于图片而是用于超链接。我认为适当的方法是:
```java @Override public void onLoadResource(WebView view, String url) ```
该方法会被调用以加载 Webview 中的每个资源(包括图片、样式表和脚本),但由于它是一个 void 类型的方法,我没有找到一种方法来更改 URL 并替换它以加载本地资源...

是的,使用这种方法我已经加载了所有的url(根url和所有资源)。但问题仍然是 - 如何覆盖加载=(我有一些关于如何替换图像内容的想法,但仍不清楚如何覆盖原始调用。 - Olegas

9

这是我在应用中使用的解决方案。

我有几个带有css / js / img和font文件的资源文件夹。

应用程序获取所有文件名,并查看是否请求了具有此名称的文件。如果是,则从资源文件夹加载它。

//get list of files of specific asset folder
        private ArrayList listAssetFiles(String path) {

            List myArrayList = new ArrayList();
            String [] list;
            try {
                list = getAssets().list(path);
                for(String f1 : list){
                    myArrayList.add(f1);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            return (ArrayList) myArrayList;
        }

        //get mime type by url
        public String getMimeType(String url) {
            String type = null;
            String extension = MimeTypeMap.getFileExtensionFromUrl(url);
            if (extension != null) {
                if (extension.equals("js")) {
                    return "text/javascript";
                }
                else if (extension.equals("woff")) {
                    return "application/font-woff";
                }
                else if (extension.equals("woff2")) {
                    return "application/font-woff2";
                }
                else if (extension.equals("ttf")) {
                    return "application/x-font-ttf";
                }
                else if (extension.equals("eot")) {
                    return "application/vnd.ms-fontobject";
                }
                else if (extension.equals("svg")) {
                    return "image/svg+xml";
                }
                type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
            }
            return type;
        }

        //return webresourceresponse
        public WebResourceResponse loadFilesFromAssetFolder (String folder, String url) {
            List myArrayList = listAssetFiles(folder);
            for (Object str : myArrayList) {
                if (url.contains((CharSequence) str)) {
                    try {
                        Log.i(TAG2, "File:" + str);
                        Log.i(TAG2, "MIME:" + getMimeType(url));
                        return new WebResourceResponse(getMimeType(url), "UTF-8", getAssets().open(String.valueOf(folder+"/" + str)));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return null;
        }

        //@TargetApi(Build.VERSION_CODES.LOLLIPOP)
        @SuppressLint("NewApi")
        @Override
        public WebResourceResponse shouldInterceptRequest(final WebView view, String url) {
            //Log.i(TAG2, "SHOULD OVERRIDE INIT");
            //String url = webResourceRequest.getUrl().toString();
            String extension = MimeTypeMap.getFileExtensionFromUrl(url);
            //I have some folders for files with the same extension
            if (extension.equals("css") || extension.equals("js") || extension.equals("img")) {
                return loadFilesFromAssetFolder(extension, url);
            }
            //more possible extensions for font folder
            if (extension.equals("woff") || extension.equals("woff2") || extension.equals("ttf") || extension.equals("svg") || extension.equals("eot")) {
                return loadFilesFromAssetFolder("font", url);
            }

            return null;
        }

4

那么检查呢?(https://dev59.com/smAg5IYBdhLWcg3wE3nQ) - Francisco Corrales Morales
3
以下是Schoenobates的示例:https://gist.github.com/kibotu/32313b957cd01258cf67 - Jan Rabe

3

一种终极解决方案是在回环上嵌入一个简单的HTTP服务器,监听您的“秘密”端口。然后,您可以将匹配的图像源URL替换为类似于http://localhost:123/.../mypic.jpg的东西。


3
这可能会有所帮助:
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
    String url = request.getUrl().toString();
    WebResourceResponse response = super.shouldInterceptRequest(view, request);
    // load native js
    if (url != null && url.contains(INJECTION_TOKEN/* scheme define */)) {

        response = new WebResourceResponse(
                "text/javascript",
                "utf-8",
                loadJsInputStream(url, JsCache.getJsFilePath(path) /* InputStream */));
    }
    return response;
}

1
这里引用了类似于 "text/javascript" 的 mimeType 参考:https://dev59.com/iGoy5IYBdhLWcg3wZdCr - WCG

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接