WebView的Android代理

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

设置 – >无线networking – >移动networking – >接入点名称 – > telkila。 现在input代理服务器地址和端口(将是80)。 WebView.enablePlatformNotifications();

但是,我可以设置从代码的代理设置? 所以我的用户不必手动设置?

谢谢

没有合法的方式来以编程方式更改您的webview代理设置。 但是可以使用javareflection来改变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; } 

我已经调整了这里提出的三个解决scheme(并修改了失败的解决scheme),以生成适用于所有Android版本的简单的setProxy方法。 我已经testing了从10到18,它适用于所有testing环境。

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

已更新2015-01-15 KitKat方法中标记为可选的部分会在Lollipop上引发exception,因为缺less辅助类,但是没有它,因此在两种情况下都基于Chromium,因此在KitKat和Lollipop上都不起作用。

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

我做了很多testing,我可以说,使用基于android.net.http.RequestQueue的重写以前的响应完美地从android 1.6到3.1。

但是有一个代码重构了API,并使其在Android 3.2和4.x上工作,这里的解决scheme:

 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) { } 

请享用

正如@Karthik所说,这些答案不适用于Android 4.4(KitKat)。 对于KitKat,答案在这里发布。

这是版本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; } 

madeye的解决schemehttps://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代理 )伪代码好:

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

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

 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"); 

首先,感谢你们所有的固定代理设置,其中不存在公众的API在Android的变化,邮政编码也帮了我很多。 我已经涵盖了所有版本的Android的问题,看到它将需要更新这个信息。 取决于上面的吉米(谢谢你!)的总结答案,我testing了它的代码在android版本3.0-3.1上运行不正确,因为android.net.ProxyProperties不被支持, updateProxy只需要通过java来reflection。 lang.Stringinput参数。 所以,我改了它的代码,希望它能帮助人们解决和我一样的问题:

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