Android-прокси WebView

Я знаю, как настроить прокси вручную и использовать его в моем WebView.

Настройки->Беспроводные сети->мобильные сети->названия точек доступа->telkila. Теперь введите адрес прокси-сервера и порт (который будет 80). WebView.enablePlatformNotifications();

Но могу ли я установить настройку прокси из кода? Значит, моему пользователю не нужно было устанавливать вручную?

Спасибо


person user430926    schedule 20.12.2010    source источник
comment
Есть библиотека от Guardian Project github.com/guardianproject/NetCipher   -  person Vlad    schedule 14.02.2020
comment
@Vlad, как я могу использовать этот проект, чтобы разблокировать свой URL-адрес в webView   -  person engmms    schedule 28.11.2020


Ответы (10)


Не существует законного способа программно изменить настройки прокси-сервера веб-просмотра. Но можно использовать отражение java для изменения значения mProxyHost из класса android.net.http.RequestQueue. Это приватное значение и для него нет сеттеров, поэтому отражение кажется единственно возможным вариантом. Я использовал это в своем проекте, и это работает. Вот пример моего метода:

    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;      
}
person amukhachov    schedule 19.04.2011
comment
не могли бы вы опубликовать код для getFieldValueSafely(requestQueueField, network); С уважением Muthuvel.P - person Muthuvel P; 13.08.2011
comment
Спасибо за блестящий кусок кода! Я добавил его в библиотеку прокси-серверов Android, чтобы предоставить всем заинтересованным пользователям Android разработчикам простой и быстрый способ поддержки прокси в своих приложениях. Еще раз спасибо! - person lechuckcaptain; 07.07.2012
comment
Я пытаюсь поместить этот код перед loadURl. Но это не работает. Где я должен поместить этот код, thz - person Bear; 16.05.2013
comment
Пожалуйста, проверьте ответ ниже, если вы используете Android API выше 11. Он должен работать до loadURl - person amukhachov; 16.05.2013
comment
Да, я пытался. Наконец, я обнаружил, что это работает в 4.1, но не в 4.2.2. - person Bear; 17.05.2013
comment
Что это? synchronized (synchronizer) { - person Nicolas; 22.05.2013
comment
закрытое поле: Синхронизатор объекта = новый объект(); - person amukhachov; 22.05.2013
comment
@birdy: это устанавливает прокси только для работающего приложения или для всей системы Android? - person koenmetsu; 13.06.2013
comment
Привет, где этот класс ProxyManager? Я скопировал и вставил приведенный ниже код и получил синтаксические ошибки для ProxyManager. кажется, вам не хватает кода класса ProxyManager - person Jonathan; 01.07.2013
comment
изменить: я удалил тег proxyManager, и это исправил ошибки synatx. однако сам код не работает. сеть = getFieldValueSafely (networkField, null); всегда возвращает ноль - person Jonathan; 01.07.2013
comment
Привет всем, можем ли мы передать информацию об аутентификации методу setProxyJB? - person toufik_at; 15.04.2014
comment
Принятый ответ перестал работать для предварительной версии android-l. У кого-нибудь есть что-нибудь для этого? - person nubela; 12.08.2014
comment
Вам следует попробовать решение из ответа Джимми Ди или этой библиотеки: github .com/shouldit/android-proxy/tree/master/ . Reflection — это просто подход, разные версии Android требуют разных реализаций. - person amukhachov; 13.08.2014
comment
@birdy Есть комментарии о том, как сбросить/удалить примененный прокси-сервер на Android WebView с помощью отражения? Пожалуйста, ответьте на этот вопрос на SO: stackoverflow.com/questions/39718818/ - person Perry; 27.09.2016
comment
@Perry Вы можете получить значение из proxyHostField перед его изменением и сохранить его как локальную переменную. Чем в reset() просто установите это предыдущее значение, используя setProxyHostField (previousHostField). - person amukhachov; 27.09.2016

Я адаптировал три решения, представленные здесь (и изменил одно, где оно не удалось), чтобы создать единый простой метод setProxy, который работает для всех версий Android. Я тестировал его с 10 по 18, и он работает во всех протестированных средах.

ОБНОВЛЕНО 4 апреля 2014 г. Наконец-то я работал над решением из комментария ниже, любезно предоставленного nubela и xjy2061. Теперь это работает для всех актуальных версий Android, включая KitKat 4.4. Если вы реализуете свой собственный подкласс Application, укажите имя класса в качестве необязательного четвертого параметра.

ОБНОВЛЕНО 15 января 2015 г. Часть, помеченная как необязательная в методе KitKat, вызывает исключение для Lollipop, поскольку отсутствует вспомогательный класс, но без этого он работает как для KitKat, так и для Lollipop, поскольку WebView основан на Хром в обоих случаях.

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;
}
person Jimmy Dee    schedule 26.08.2013
comment
Не подскажете, как вернуть это обратно, что означает очистку прокси - person mohitum; 03.12.2013
comment
@ mohitum007 mohitum007 Я предлагаю для 2.x вы можете читать поля прокси-сервера корреспондента и восстанавливать их, когда вам нужно отключить прокси-сервер. Для 3.x и 4.x похоже, что прокси настроен только для одного экземпляра объекта WebView, а не для всей системы, не так ли? - person Timur Gilfanov; 08.12.2013
comment
Вот аналогичный фрагмент, который, как сообщается, работает на KitKat/4.4: kitkat">stackoverflow.com/questions/19979578/ - person vitriolix; 13.02.2014
comment
Привет, Можем ли мы добавить прокси-аутентификацию в методе setProxyJB? Спасибо ! - person toufik_at; 15.04.2014
comment
Я просто подумал, что мне жаль, что у меня есть популярный общий ответ. Может быть, это где-то в Android-вики, где каждый может это отредактировать. Это в основном скопировано из моего собственного проекта, где я использую его по мере необходимости (без авторизации или возможности очистить прокси). github.com/jdee/ dubsar_android/blob/master/Dubsar/src/com/ Это GPL. Не стесняйтесь использовать его, если это соответствует вашим потребностям. Также не стесняйтесь копировать и расширять мой ответ здесь, если вы хотите добавить к нему. - person Jimmy Dee; 17.04.2014
comment
Похоже, что 4.4.4 по-прежнему относится к уровню API 19, в отличие от 4.4.W или L (?), которые содержат 20. Единственный доступ, который у меня есть к 19, — это эмулятор, то есть 4.4.2. Там сработало. Вы можете посмотреть/опубликовать свой вопрос в этой теме, где я получил ответ API 19 в первую очередь: stackoverflow.com/questions/19979578/ - person Jimmy Dee; 29.06.2014
comment
У меня проблема с 4.2.2 и телефоном Alcatel One Touch 6040X (Idol X). Насколько я понимаю, это относится как раз к телефонам Alcatel! У меня есть еще один телефон с 4.2.2 - Samsung GT-S7580 и код работает отлично. - person Sasa Tancev; 22.07.2014
comment
пожалуйста, добавьте следующий ответ к вашему решению (для API›19): stackoverflow.com/a/25485747/365229 - person Behrouz.M; 17.01.2019

Я сделал много тестов и могу сказать, что предыдущий ответ с использованием переопределения на основе 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)
{    
}

наслаждаться

person Guillaume13    schedule 16.04.2012
comment
oauthPage что это в вашем фрагменте кода дает ошибку компиляции - person RPB; 30.08.2012
comment
@Rinkalkumar передайте ссылку на ваш объект WebView. - person PC.; 30.08.2012
comment
Спасибо. это работает только в 4.0 ICS. но не будет работать с JB и GB. - person Nicolas; 22.05.2013
comment
Привет. Можем ли мы добавить прокси-аутентификацию с помощью этого метода? - person toufik_at; 15.04.2014

Как сказал @Karthik, эти ответы не работают на Android 4.4 (KitKat). Для KitKat ответ был опубликован здесь.

person xjy2061    schedule 29.12.2013

Это код для версий 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;
}
person MediumOne    schedule 09.01.2013
comment
@Bear - я проверял это на многих устройствах, и все работает отлично. Можете ли вы сказать, где он терпит неудачу? - person MediumOne; 17.05.2013
comment
Я разместил свой вопрос здесь stackoverflow.com/ вопросы/16590734/ Спасибо. - person Bear; 18.05.2013
comment
Привет, Можем ли мы добавить прокси-аутентификацию в методе setProxyJB? Спасибо ! - person toufik_at; 15.04.2014

Chromium начал запутывать имена классов в новом выпуске (имя класса для ProxyChangeListener выглядит как «bMh» в последней версии и может измениться в будущих выпусках. Итак, Решение Джимми Ди больше не будет работать для более новых версий, поскольку следующая проверка всегда завершается ошибкой:

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

Решение, о котором я думал, состоит в том, чтобы вызывать onReceive для всех широковещательных приемников в текущем контексте приложения.

Теперь я понимаю, что это не идеальное решение, потому что оно может иметь нежелательные побочные эффекты из-за вызова неправильных приемников, и нужно тщательно изучить все широковещательные приемники в контексте приложения, чтобы убедиться, что они не делают ничего плохого. (Есть другие идеи?)

Основываясь на предыдущем ответе, это будет примерно так:

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

Будет очень полезно, если у кого-то в сообществе есть идея получше.

person Amer Sheeny    schedule 20.06.2018

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;

Я удалил использование applicationclassname. хорошо работает на андроид 7.1

person lvshow    schedule 01.02.2020

решение 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

лучше, чем птичий ответ (прокси-сервер Android WebView): псевдокод:

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

и лучше, чем ответ Guillaume13 и MediumOne (прокси-сервер Android WebView):

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");
person diyism    schedule 13.07.2013

Во-первых, спасибо всем вам за почтовые коды для исправления настроек прокси, которые не существуют в публичном API для изменений в Android, это также мне очень помогло. Я рассмотрел проблемы во всех версиях Android и вижу, что эту информацию нужно будет обновить. В зависимости от сводного ответа Джимми выше (спасибо!), я проверил, что его код работает неправильно на 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;
    }
}
person Fuong Lee    schedule 15.04.2015

когда 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
    }
}
person dujianchi    schedule 08.02.2021