给 WebView 设置代理

作者: crianzy | 来源:发表于2016-07-27 16:24 被阅读4899次

    最近由于工作需要, 一些国外的电商网站什么的国内用户打不开, 所以就想说能不能我们手动给 WebView 加上一层代理
    然后在网上 google 了一圈, 发现还是有些 相关代码可以借鉴的.

    但是都只是设置了代理, 没有很好的把代理给关掉.
    因为多数情况下, 设置的代理都是整个 app 全局的,会导致所有 webView打开的网站都走了代理, 这样就不好了

    下面是我整理的关于 webView 代理的相关类

    package com.zuimeia.ui.webview;
    
    public class SettingProxy {
        private static final String LOG_TAG = "halfman";
    
        private static final String APPLICATION_NAME = "android.app.Application";
    
        // 注意这里applicationClassName 传递的是 application 的类名
        public static boolean setProxy(WebView webview, String host, int port, String applicationClassName) {
            // 3.2 (HC) or lower
            if (Build.VERSION.SDK_INT <= 13) {
                return setProxyUpToHC(webview, host, port);
            }
            // ICS: 4.0
            else if (Build.VERSION.SDK_INT <= 15) {
                return setProxyICS(webview, host, port);
            }
            // 4.1-4.3 (JB)
            else if (Build.VERSION.SDK_INT <= 18) {
                return setProxyJB(webview, host, port);
            }
            // 4.4 (KK) & 5.0 (Lollipop)
            else {
                return setProxyKKPlus(webview, host, port, applicationClassName);
            }
        }
    
        public static boolean revertBackProxy(WebView webview, String applicationClassName) {
            // 3.2 (HC) or lower
            if (Build.VERSION.SDK_INT <= 13) {
                return true;
            }
            // ICS: 4.0
            else if (Build.VERSION.SDK_INT <= 15) {
                return revertProxyICS(webview);
            }
            // 4.1-4.3 (JB)
            else if (Build.VERSION.SDK_INT <= 18) {
                return revertProxyJB(webview);
            }
            // 4.4 (KK) & 5.0 (Lollipop)
            else {
                return revertProxyKKPlus(webview, applicationClassName);
            }
        }
    
        /**
         * Set Proxy for Android 3.2 and below.
         */
        @SuppressWarnings("all")
        private static boolean setProxyUpToHC(WebView webview, String host, int port) {
            Log.d(LOG_TAG, "Setting proxy with <= 3.2 API.");
    
            HttpHost proxyServer = new HttpHost(host, port);
            // Getting network
            Class networkClass = null;
            Object network = null;
            try {
                networkClass = Class.forName("android.webkit.Network");
                if (networkClass == null) {
                    Log.e(LOG_TAG, "failed to get class for android.webkit.Network");
                    return false;
                }
                Method getInstanceMethod = networkClass.getMethod("getInstance", Context.class);
                if (getInstanceMethod == null) {
                    Log.e(LOG_TAG, "failed to get getInstance method");
                }
                network = getInstanceMethod.invoke(networkClass, new Object[]{webview.getContext()});
            } catch (Exception ex) {
                Log.e(LOG_TAG, "error getting network: " + ex);
                return false;
            }
            if (network == null) {
                Log.e(LOG_TAG, "error getting network: network is null");
                return false;
            }
            Object requestQueue = null;
            try {
                Field requestQueueField = networkClass
                        .getDeclaredField("mRequestQueue");
                requestQueue = getFieldValueSafely(requestQueueField, network);
            } catch (Exception ex) {
                Log.e(LOG_TAG, "error getting field value");
                return false;
            }
            if (requestQueue == null) {
                Log.e(LOG_TAG, "Request queue is null");
                return false;
            }
            Field proxyHostField = null;
            try {
                Class requestQueueClass = Class.forName("android.net.http.RequestQueue");
                proxyHostField = requestQueueClass
                        .getDeclaredField("mProxyHost");
            } catch (Exception ex) {
                Log.e(LOG_TAG, "error getting proxy host field");
                return false;
            }
    
            boolean temp = proxyHostField.isAccessible();
            try {
                proxyHostField.setAccessible(true);
                proxyHostField.set(requestQueue, proxyServer);
            } catch (Exception ex) {
                Log.e(LOG_TAG, "error setting proxy host");
            } finally {
                proxyHostField.setAccessible(temp);
            }
    
            Log.d(LOG_TAG, "Setting proxy with <= 3.2 API successful!");
            return true;
        }
    
        @SuppressWarnings("all")
        private static boolean setProxyICS(WebView webview, String host, int port) {
            try {
                Log.d(LOG_TAG, "Setting proxy with 4.0 API.");
    
                Class jwcjb = Class.forName("android.webkit.JWebCoreJavaBridge");
                Class params[] = new Class[1];
                params[0] = Class.forName("android.net.ProxyProperties");
                Method updateProxyInstance = jwcjb.getDeclaredMethod("updateProxy", params);
    
                Class wv = Class.forName("android.webkit.WebView");
                Field mWebViewCoreField = wv.getDeclaredField("mWebViewCore");
                Object mWebViewCoreFieldInstance = getFieldValueSafely(mWebViewCoreField, webview);
    
                Class wvc = Class.forName("android.webkit.WebViewCore");
                Field mBrowserFrameField = wvc.getDeclaredField("mBrowserFrame");
                Object mBrowserFrame = getFieldValueSafely(mBrowserFrameField, mWebViewCoreFieldInstance);
    
                Class bf = Class.forName("android.webkit.BrowserFrame");
                Field sJavaBridgeField = bf.getDeclaredField("sJavaBridge");
                Object sJavaBridge = getFieldValueSafely(sJavaBridgeField, mBrowserFrame);
    
                Class ppclass = Class.forName("android.net.ProxyProperties");
                Class pparams[] = new Class[3];
                pparams[0] = String.class;
                pparams[1] = int.class;
                pparams[2] = String.class;
                Constructor ppcont = ppclass.getConstructor(pparams);
    
                updateProxyInstance.invoke(sJavaBridge, ppcont.newInstance(host, port, null));
    
                Log.d(LOG_TAG, "Setting proxy with 4.0 API successful!");
                return true;
            } catch (Exception ex) {
                Log.e(LOG_TAG, "failed to set HTTP proxy: " + ex);
                return false;
            }
        }
    
        private static boolean revertProxyICS(WebView webview) {
            try {
                Log.d(LOG_TAG, "Setting proxy with 4.0 API.");
    
                Class jwcjb = Class.forName("android.webkit.JWebCoreJavaBridge");
                Class params[] = new Class[1];
                params[0] = Class.forName("android.net.ProxyProperties");
                Method updateProxyInstance = jwcjb.getDeclaredMethod("updateProxy", params);
    
                Class wv = Class.forName("android.webkit.WebView");
                Field mWebViewCoreField = wv.getDeclaredField("mWebViewCore");
                Object mWebViewCoreFieldInstance = getFieldValueSafely(mWebViewCoreField, webview);
    
                Class wvc = Class.forName("android.webkit.WebViewCore");
                Field mBrowserFrameField = wvc.getDeclaredField("mBrowserFrame");
                Object mBrowserFrame = getFieldValueSafely(mBrowserFrameField, mWebViewCoreFieldInstance);
    
                Class bf = Class.forName("android.webkit.BrowserFrame");
                Field sJavaBridgeField = bf.getDeclaredField("sJavaBridge");
                Object sJavaBridge = getFieldValueSafely(sJavaBridgeField, mBrowserFrame);
    
                Class ppclass = Class.forName("android.net.ProxyProperties");
                Class pparams[] = new Class[3];
                pparams[0] = String.class;
                pparams[1] = int.class;
                pparams[2] = String.class;
                Constructor ppcont = ppclass.getConstructor(pparams);
    
                Object o = null;
                updateProxyInstance.invoke(sJavaBridge, o);
    
                Log.d(LOG_TAG, "Setting proxy with 4.0 API successful!");
                return true;
            } catch (Exception ex) {
                Log.e(LOG_TAG, "failed to set HTTP proxy: " + ex);
                return false;
            }
        }
    
        /**
         * Set Proxy for Android 4.1 - 4.3.
         */
        @SuppressWarnings("all")
        private static boolean setProxyJB(WebView webview, String host, int port) {
            Log.d(LOG_TAG, "Setting proxy with 4.1 - 4.3 API.");
    
            try {
                Class wvcClass = Class.forName("android.webkit.WebViewClassic");
                Class wvParams[] = new Class[1];
                wvParams[0] = Class.forName("android.webkit.WebView");
                Method fromWebView = wvcClass.getDeclaredMethod("fromWebView", wvParams);
                Object webViewClassic = fromWebView.invoke(null, webview);
    
                Class wv = Class.forName("android.webkit.WebViewClassic");
                Field mWebViewCoreField = wv.getDeclaredField("mWebViewCore");
                Object mWebViewCoreFieldInstance = getFieldValueSafely(mWebViewCoreField, webViewClassic);
    
                Class wvc = Class.forName("android.webkit.WebViewCore");
                Field mBrowserFrameField = wvc.getDeclaredField("mBrowserFrame");
                Object mBrowserFrame = getFieldValueSafely(mBrowserFrameField, mWebViewCoreFieldInstance);
    
                Class bf = Class.forName("android.webkit.BrowserFrame");
                Field sJavaBridgeField = bf.getDeclaredField("sJavaBridge");
                Object sJavaBridge = getFieldValueSafely(sJavaBridgeField, mBrowserFrame);
    
                Class ppclass = Class.forName("android.net.ProxyProperties");
                Class pparams[] = new Class[3];
                pparams[0] = String.class;
                pparams[1] = int.class;
                pparams[2] = String.class;
                Constructor ppcont = ppclass.getConstructor(pparams);
    
                Class jwcjb = Class.forName("android.webkit.JWebCoreJavaBridge");
                Class params[] = new Class[1];
                params[0] = Class.forName("android.net.ProxyProperties");
                Method updateProxyInstance = jwcjb.getDeclaredMethod("updateProxy", params);
    
                updateProxyInstance.invoke(sJavaBridge, ppcont.newInstance(host, port, null));
            } catch (Exception ex) {
                Log.e(LOG_TAG, "Setting proxy with >= 4.1 API failed with error: " + ex.getMessage());
                return false;
            }
    
            Log.d(LOG_TAG, "Setting proxy with 4.1 - 4.3 API successful!");
            return true;
        }
    
        private static boolean revertProxyJB(WebView webview) {
            Log.d(LOG_TAG, "revert proxy with 4.1 - 4.3 API.");
    
            try {
                Class wvcClass = Class.forName("android.webkit.WebViewClassic");
                Class wvParams[] = new Class[1];
                wvParams[0] = Class.forName("android.webkit.WebView");
                Method fromWebView = wvcClass.getDeclaredMethod("fromWebView", wvParams);
                Object webViewClassic = fromWebView.invoke(null, webview);
    
                Class wv = Class.forName("android.webkit.WebViewClassic");
                Field mWebViewCoreField = wv.getDeclaredField("mWebViewCore");
                Object mWebViewCoreFieldInstance = getFieldValueSafely(mWebViewCoreField, webViewClassic);
    
                Class wvc = Class.forName("android.webkit.WebViewCore");
                Field mBrowserFrameField = wvc.getDeclaredField("mBrowserFrame");
                Object mBrowserFrame = getFieldValueSafely(mBrowserFrameField, mWebViewCoreFieldInstance);
    
                Class bf = Class.forName("android.webkit.BrowserFrame");
                Field sJavaBridgeField = bf.getDeclaredField("sJavaBridge");
                Object sJavaBridge = getFieldValueSafely(sJavaBridgeField, mBrowserFrame);
    
                Class ppclass = Class.forName("android.net.ProxyProperties");
                Class pparams[] = new Class[3];
                pparams[0] = String.class;
                pparams[1] = int.class;
                pparams[2] = String.class;
                Constructor ppcont = ppclass.getConstructor(pparams);
    
                Class jwcjb = Class.forName("android.webkit.JWebCoreJavaBridge");
                Class params[] = new Class[1];
                params[0] = Class.forName("android.net.ProxyProperties");
                Method updateProxyInstance = jwcjb.getDeclaredMethod("updateProxy", params);
    
                Object o = null;
                updateProxyInstance.invoke(sJavaBridge, o);
            } catch (Exception ex) {
                Log.e(LOG_TAG, "Setting proxy with >= 4.1 API failed with error: " + ex.getMessage());
                return false;
            }
    
            Log.d(LOG_TAG, "revert proxy with 4.1 - 4.3 API successful!");
            return true;
        }
    
        // from https://stackoverflow.com/questions/19979578/android-webview-set-proxy-programatically-kitkat
        @SuppressLint("NewApi")
        @SuppressWarnings("all")
        private static boolean setProxyKKPlus(WebView webView, String host, int port, String applicationClassName) {
            Log.d(LOG_TAG, "Setting proxy with >= 4.4 API.");
    
            Context appContext = webView.getContext().getApplicationContext();
            System.setProperty("http.proxyHost", host);
            System.setProperty("http.proxyPort", port + "");
            System.setProperty("https.proxyHost", host);
            System.setProperty("https.proxyPort", port + "");
            try {
                Class applictionCls = Class.forName(applicationClassName);
                Field loadedApkField = applictionCls.getField("mLoadedApk");
                loadedApkField.setAccessible(true);
                Object loadedApk = loadedApkField.get(appContext);
                Class loadedApkCls = Class.forName("android.app.LoadedApk");
                Field receiversField = loadedApkCls.getDeclaredField("mReceivers");
                receiversField.setAccessible(true);
                ArrayMap receivers = (ArrayMap) receiversField.get(loadedApk);
                for (Object receiverMap : receivers.values()) {
                    for (Object rec : ((ArrayMap) receiverMap).keySet()) {
                        Class clazz = rec.getClass();
                        if (clazz.getName().contains("ProxyChangeListener")) {
                            Method onReceiveMethod = clazz.getDeclaredMethod("onReceive", Context.class, Intent.class);
                            Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
    
                            onReceiveMethod.invoke(rec, appContext, intent);
                        }
                    }
                }
    
                Log.d(LOG_TAG, "Setting proxy with >= 4.4 API successful!");
                return true;
            } catch (ClassNotFoundException e) {
                StringWriter sw = new StringWriter();
                e.printStackTrace(new PrintWriter(sw));
                String exceptionAsString = sw.toString();
                Log.v(LOG_TAG, e.getMessage());
                Log.v(LOG_TAG, exceptionAsString);
            } catch (NoSuchFieldException e) {
                StringWriter sw = new StringWriter();
                e.printStackTrace(new PrintWriter(sw));
                String exceptionAsString = sw.toString();
                Log.v(LOG_TAG, e.getMessage());
                Log.v(LOG_TAG, exceptionAsString);
            } catch (IllegalAccessException e) {
                StringWriter sw = new StringWriter();
                e.printStackTrace(new PrintWriter(sw));
                String exceptionAsString = sw.toString();
                Log.v(LOG_TAG, e.getMessage());
                Log.v(LOG_TAG, exceptionAsString);
            } catch (IllegalArgumentException e) {
                StringWriter sw = new StringWriter();
                e.printStackTrace(new PrintWriter(sw));
                String exceptionAsString = sw.toString();
                Log.v(LOG_TAG, e.getMessage());
                Log.v(LOG_TAG, exceptionAsString);
            } catch (NoSuchMethodException e) {
                StringWriter sw = new StringWriter();
                e.printStackTrace(new PrintWriter(sw));
                String exceptionAsString = sw.toString();
                Log.v(LOG_TAG, e.getMessage());
                Log.v(LOG_TAG, exceptionAsString);
            } catch (InvocationTargetException e) {
                StringWriter sw = new StringWriter();
                e.printStackTrace(new PrintWriter(sw));
                String exceptionAsString = sw.toString();
                Log.v(LOG_TAG, e.getMessage());
                Log.v(LOG_TAG, exceptionAsString);
            }
            return false;
        }
    
        @SuppressLint("NewApi")
        @SuppressWarnings("all")
        private static boolean revertProxyKKPlus(WebView webView, String applicationClassName) {
    
            Context appContext = webView.getContext().getApplicationContext();
            Properties properties = System.getProperties();
    
            properties.remove("http.proxyHost");
            properties.remove("http.proxyPort");
            properties.remove("https.proxyHost");
            properties.remove("https.proxyPort");
            try {
                Class applictionCls = Class.forName(applicationClassName);
                Field loadedApkField = applictionCls.getField("mLoadedApk");
                loadedApkField.setAccessible(true);
                Object loadedApk = loadedApkField.get(appContext);
                Class loadedApkCls = Class.forName("android.app.LoadedApk");
                Field receiversField = loadedApkCls.getDeclaredField("mReceivers");
                receiversField.setAccessible(true);
                ArrayMap receivers = (ArrayMap) receiversField.get(loadedApk);
                for (Object receiverMap : receivers.values()) {
                    for (Object rec : ((ArrayMap) receiverMap).keySet()) {
                        Class clazz = rec.getClass();
                        if (clazz.getName().contains("ProxyChangeListener")) {
                            Method onReceiveMethod = clazz.getDeclaredMethod("onReceive", Context.class, Intent.class);
                            Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
    //                        intent.putExtra("proxy", null);
                            onReceiveMethod.invoke(rec, appContext, intent);
                        }
                    }
                }
                Log.d(LOG_TAG, "Revert proxy with >= 4.4 API successful!");
                return true;
            } catch (ClassNotFoundException e) {
                StringWriter sw = new StringWriter();
                e.printStackTrace(new PrintWriter(sw));
                String exceptionAsString = sw.toString();
                Log.v(LOG_TAG, e.getMessage());
                Log.v(LOG_TAG, exceptionAsString);
            } catch (NoSuchFieldException e) {
                StringWriter sw = new StringWriter();
                e.printStackTrace(new PrintWriter(sw));
                String exceptionAsString = sw.toString();
                Log.v(LOG_TAG, e.getMessage());
                Log.v(LOG_TAG, exceptionAsString);
            } catch (IllegalAccessException e) {
                StringWriter sw = new StringWriter();
                e.printStackTrace(new PrintWriter(sw));
                String exceptionAsString = sw.toString();
                Log.v(LOG_TAG, e.getMessage());
                Log.v(LOG_TAG, exceptionAsString);
            } catch (IllegalArgumentException e) {
                StringWriter sw = new StringWriter();
                e.printStackTrace(new PrintWriter(sw));
                String exceptionAsString = sw.toString();
                Log.v(LOG_TAG, e.getMessage());
                Log.v(LOG_TAG, exceptionAsString);
            } catch (NoSuchMethodException e) {
                StringWriter sw = new StringWriter();
                e.printStackTrace(new PrintWriter(sw));
                String exceptionAsString = sw.toString();
                Log.v(LOG_TAG, e.getMessage());
                Log.v(LOG_TAG, exceptionAsString);
            } catch (InvocationTargetException e) {
                StringWriter sw = new StringWriter();
                e.printStackTrace(new PrintWriter(sw));
                String exceptionAsString = sw.toString();
                Log.v(LOG_TAG, e.getMessage());
                Log.v(LOG_TAG, exceptionAsString);
            }
            return false;
        }
    
        private static Object getFieldValueSafely(Field field, Object classInstance) throws IllegalArgumentException, IllegalAccessException {
            boolean oldAccessibleValue = field.isAccessible();
            field.setAccessible(true);
            Object result = field.get(classInstance);
            field.setAccessible(oldAccessibleValue);
            return result;
        }
    
    }
    
    

    注意

    在当前 Web 关闭后, 需要调用revertBackProxy 来恢复代理, 不然以后打开的所有 WebView 都可能走代理.

    相关文章

      网友评论

        本文标题:给 WebView 设置代理

        本文链接:https://www.haomeiwen.com/subject/tnbojttx.html