安卓WebView代理

42

我知道如何手动设置代理并在我的WebView中使用它。

设置 -> 无线网络 -> 移动网络 -> 接入点名称 -> telkila。现在输入代理服务器地址和端口号(将为80)。 WebView.enablePlatformNotifications();

但我能否从代码中设置代理设置? 这样我的用户就不必手动设置了吗?

谢谢


1
这里有一个来自Guardian Project的库 https://github.com/guardianproject/NetCipher - Vlad
@Vlad,我该如何使用这个项目在webView中解除我的URL的阻止? - engmms
10个回答

36

我已经调整了这里提出的三个解决方案(并在其中一个失败时进行了修改),以生成一个适用于所有Android版本的单一简单setProxy方法。 我已经测试过从10到18的所有测试环境,它都可以正常工作。

更新于2014-04-04 最终我采用了下面评论中提供的解决方案,由nubela和xjy2061提供。现在,这适用于所有当前的Android版本,包括KitKat 4.4。如果您实现自己的Application子类,请将该类的名称作为可选的第四个参数。

更新于2015-01-15 KitKat方法中标记为可选的部分会因缺少辅助类而在Lollipop上抛出异常,但是没有那个部分它在KitKat和Lollipop上都能正常工作,因为WebView在两种情况下都基于Chromium。

public static boolean setProxy(WebView webview, String host, int port, String applicationClassName="android.app.Application") {
    // 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);
    }
}

/**
 * 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;
    }
}

/**
 * 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;
}

// from https://dev59.com/43nZa4cB1Zd3GeqPmSJf
@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;
}

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;
}

你能告诉我如何恢复它吗?也就是清除代理。 - mohitum
我建议对于2.x版本,您可以读取相应代理文件并在需要关闭代理时进行还原。对于3.x和4.x版本,看起来代理设置仅适用于WebView对象的一个实例,而不是系统范围内的所有实例,是吗? - Timur Gilfanov
这里有一个类似的片段,据说可以在KitKat / 4.4上运行:https://dev59.com/43nZa4cB1Zd3GeqPmSJf - vitriolix
你好, 在setProxyJB方法中能否添加代理验证? 谢谢! - toufik_at
1
请将以下答案添加到您的解决方案中(适用于API>19):https://stackoverflow.com/a/25485747/365229 - Behrouz.M
显示剩余3条评论

25

没有合法的方法可以在程序中更改您的webview代理设置。但是可以使用Java反射从android.net.http.RequestQueue类更改mProxyHost值。它是私有值,并且没有为其提供设置器,因此反射似乎是唯一可能的变体。我在我的项目中使用了它,它起作用。这是我的方法示例:

    private boolean setProxyHostField(HttpHost proxyServer) {
    // Getting network      
    Class networkClass = null;
    Object network = null;
    try {
        networkClass = Class.forName("android.webkit.Network");
        Field networkField = networkClass.getDeclaredField("sNetwork");
        network = getFieldValueSafely(networkField, null);
    } catch (Exception ex) {
        Log.e(ProxyManager.class.getName(), "error getting network");
        return false;
    }
    if (network == null) {
        Log.e(ProxyManager.class.getName(), "error getting network : null");
        return false;
    }
    Object requestQueue = null;
    try {
        Field requestQueueField = networkClass
                .getDeclaredField("mRequestQueue");
        requestQueue = getFieldValueSafely(requestQueueField, network);
    } catch (Exception ex) {
        Log.e(ProxyManager.class.getName(), "error getting field value");
        return false;
    }
    if (requestQueue == null) {
        Log.e(ProxyManager.class.getName(), "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(ProxyManager.class.getName(), "error getting proxy host field");
        return false;
    }       
    synchronized (synchronizer) {
        boolean temp = proxyHostField.isAccessible();
        try {
            proxyHostField.setAccessible(true);
            proxyHostField.set(requestQueue, proxyServer);
        } catch (Exception ex) {
            Log.e(ProxyManager.class.getName(), "error setting proxy host");
        } finally {
            proxyHostField.setAccessible(temp);
        }
    }
    return true;
}

private 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;      
}

1
请问您能否发布getFieldValueSafely(requestQueueField, network)的代码?谢谢。 敬礼, Muthuvel.P - Muthuvel P
2
感谢您提供如此精彩的代码!我已将其添加到Android代理库中,以便为所有有兴趣的Android开发人员提供一种简单快捷的方式来支持他们应用程序中的代理。再次感谢您! - lechuckcaptain
1
如果您使用的是 Android API 11 及以上版本,请在 loadURL 前检查下面的答案,它应该可以正常工作。 - amukhachov
1
私有字段:Object synchronizer = new Object(); - amukhachov
嗨,这个ProxyManager类在哪里?我复制并粘贴了下面的代码,但是ProxyManager出现了语法错误。看起来你漏掉了ProxyManager类的代码。 - Jono
显示剩余11条评论

9

我进行了许多测试,并且可以说,先前使用基于android.net.http.RequestQueue的覆盖方法可以完美地在Android 1.6到3.1上运行。

但是,在API上进行了代码重构,为了使其在Android 3.2和4.x上工作,这里是解决方案:

try
{
  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 mWebViewCoreFieldIntance = getFieldValueSafely(mWebViewCoreField, oauthPage);

  Class wvc = Class.forName("android.webkit.WebViewCore");
  Field mBrowserFrameField = wvc.getDeclaredField("mBrowserFrame");
  Object mBrowserFrame = getFieldValueSafely(mBrowserFrameField, mWebViewCoreFieldIntance);

  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("my.proxy.com", 1234, null)); 
}
catch (Exception ex)
{    
}

享受


1
oauthPage是你的代码片段中的什么?它导致编译错误。 - RPB
2
@Rinkalkumar,请传递您的 WebView 对象的引用。 - PC.
1
谢谢。它只在4.0 ICS中工作,但无法与JB和GB一起使用。 - Nicolas
你好, 我们能否使用这种方法添加代理身份验证? - toufik_at

8

当API >= 29时:

implementation 'androidx.webkit:webkit:1.4.0'

将以下内容添加到app/build.gradle文件中,然后

private fun setProxy(host: String, port: Int) {
    if (WebViewFeature.isFeatureSupported(WebViewFeature.PROXY_OVERRIDE)) {
        val proxyUrl = "${host}:${port}"
        val proxyConfig: ProxyConfig = ProxyConfig.Builder()
            .addProxyRule(proxyUrl)
            .addDirect()//when proxy is not working, use direct connect, maybe?
            .build()
        ProxyController.getInstance().setProxyOverride(proxyConfig, object : Executor {
            override fun execute(command: Runnable) {

            }
        }, Runnable { Log.w(TAG, "WebView proxy") })
    } else {
        // use the solution of other anwsers
    }
}

在哪里加密码?它只有主机和端口吗? - Ahmad
@Ahmad 我猜测是在 WebViewClient.onReceivedHttpAuthRequest 中处理身份验证。 - dujianchi

3

正如@Karthik所说,这些答案在Android 4.4(KitKat)上不起作用。 针对KitKat的答案发布在这里


1

Chromium在新版本中开始混淆类名(最新版本中ProxyChangeListener的类名似乎是“bMh”,并且可能会在将来的版本中更改。因此,Jimmy Dee的解决方案对于更新的版本将不再起作用,因为以下检查将始终失败:

if (clazz.getName().contains("ProxyChangeListener"))

我想到的解决方案是在当前应用程序上下文中调用所有广播接收器上的onReceive方法。
现在我明白这不是一个理想的解决方案,因为它可能通过调用错误的接收器产生意外的副作用,并且应该仔细检查应用程序上下文中的所有广播接收器,以确保它们不会做任何有害的事情。(还有其他想法吗?)
在之前的答案基础上,代码将如下所示:
// from https://dev59.com/43nZa4cB1Zd3GeqPmSJf
@SuppressLint("NewApi")
@SuppressWarnings("all")
private static boolean setProxyKKPlus(WebView webView, String host, int port, String applicationClassName, Context appContext) {
    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);
        ArrayMap contextReceivers = (ArrayMap) receivers.get(appContext);
        for (Object rec : contextReceivers.keySet()) {
            Class clazz = rec.getClass();

            Method onReceiveMethod = clazz.getDeclaredMethod("onReceive", Context.class, Intent.class);
            Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
            try {
                onReceiveMethod.invoke(rec, appContext, intent);
            } catch (Exception e) {
                // oops, couldn't invoke this receiver
            }
        }


        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;
}

如果社区中有人有更好的想法,那将非常有帮助。


1
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 {
    Field loadedApkField = appContext.getClass().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;

我移除了应用程序类名的使用。它在Android 7.1上运行良好。


0
首先,感谢大家提供修复代理设置的代码,因为在Android中没有公共API可以更改代理设置,这些代码对我帮助很大。我已经涵盖了所有版本的Android中的问题,并且看到需要更新此信息。根据Jimmy上面的总结答案(谢谢!),我测试发现它的代码在Android 3.0-3.1版本上运行不正确,因为android.net.ProxyProperties不受支持,而updateProxy方法需要反射,只有通过java.lang.String输入参数。所以,我按照这种方式更改了代码,希望能帮助遇到同样问题的人:
/**
 * Set Proxy from 3.0.x - to 3.1.x API.
 * @param webview webview webview to apply proxy
 * @param host host name of proxy server
 * @param port port of proxy server
 * @return true/false if success or not
 */
private static boolean setProxyOnlyHC30to31(WebView webview, String host, int port) {
    try
    {
        Logger.d(ProxySettings.class, "Setting proxy from 3.0.x - 3.1.x API.");

        Class jwcjb = Class.forName("android.webkit.JWebCoreJavaBridge");
        Class params[] = new Class[1];
        params[0] = Class.forName("java.lang.String");
        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);

        updateProxyInstance.invoke(sJavaBridge, "http://" + host + ":" + port);

        Logger.d(ProxySettings.class, "Setting proxy from 3.0.x - 3.1.x API successful!");
        return true;
    }
    catch (Exception ex)
    {
        if (Helper.DEBUG) Logger.e(ProxySettings.class, "failed to set HTTP proxy: " + ex);
        return false;
    }
}

0
这是4.1和4.2版本的代码。
/**
 * Set Proxy for Android 4.1 and above.
 */
public static boolean setProxyICSPlus(WebView webview, String host, int port, String exclusionList) {

    Log.d("", "Setting proxy with >= 4.1 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 mWebViewCoreFieldIntance = getFieldValueSafely(mWebViewCoreField, webViewClassic);

        Class wvc = Class.forName("android.webkit.WebViewCore");
        Field mBrowserFrameField = wvc.getDeclaredField("mBrowserFrame");
        Object mBrowserFrame = getFieldValueSafely(mBrowserFrameField, mWebViewCoreFieldIntance);

        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, exclusionList));

    } catch (Exception ex) {
        Log.e("","Setting proxy with >= 4.1 API failed with error: " + ex.getMessage());
        return false;
    }

    Log.d("", "Setting proxy with >= 4.1 API successful!");
    return true;
}

@Bear - 我已经在很多设备上测试过了,它完美地运行。你能告诉我它失败的地方吗? - MediumOne
我已经在此发布了我的问题:http://stackoverflow.com/questions/16590734/use-proxy-in-webview-in-android谢谢。 - Bear
1
嗨,我们可以在 setProxyJB 方法中添加代理身份验证吗?谢谢! - toufik_at

0

madeye的解决方案https://gist.github.com/madeye/2297083 伪代码:

android.webkit.Network.getInstance().mRequestQueue.mProxyHost=new HttpHost(host, port, "http")    //sdk < 14
android.webkit.WebViewCore.sendStaticMessage(new android.net.ProxyProperties(...))   //sdk >= 14

比Birdy的答案(WebView android proxy)更好。伪代码:

android.webkit.Network.sNetwork.mRequestQueue.mProxyHost=new HttpHost(host, port, "http")    //sdk < 14

比Guillaume13和MediumOne的答案更好(WebView android proxy)的伪代码:

android.webkit.JWebCoreJavaBridge.updateProxy(android.webkit.WebViewClassic.fromWebView(webview).mWebViewCore.mBrowserFrame.sJavaBridge, new android.net.ProxyProperties(...))   //sdk >= 14

但我不知道为什么只有在setProxy之前插入两行代码时代理才能成功:

webview1.loadUrl("http://0.0.0.0");
try {Thread.sleep(100);} catch (Exception e) {}
ProxySettings.setProxy(getApplicationContext(), "192.168.0.109", 8081);
webview1.loadUrl("http://www.google.com/index.php");

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