Webview Android configurer le proxy par programmation Kitkat

est-ce que quelqu'un sait comment définir proxy dans android webview de manière programmatique sur la dernière version de Kitkat?

Cette SORTE de lien WebView android proxy parle à propos de la version SDK jusqu'à la version 18. Mais cette solution ne fonctionne plus avec Kitkat car l'implémentation sous-jacente de webkit est modifiée et utilise chromium maintenant.

7
demandé sur Community 2013-11-14 17:56:52

6 réponses

voici ma solution:

public static void setKitKatWebViewProxy(Context appContext, String host, int port) {
    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("android.app.Application");
        Field loadedApkField = applictionCls.getDeclaredField("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);

                    /*********** optional, may be need in future *************/
                    final String CLASS_NAME = "android.net.ProxyProperties";
                    Class cls = Class.forName(CLASS_NAME);
                    Constructor constructor = cls.getConstructor(String.class, Integer.TYPE, String.class);
                    constructor.setAccessible(true);
                    Object proxyProperties = constructor.newInstance(host, port, null);
                    intent.putExtra("proxy", (Parcelable) proxyProperties);
                    /*********** optional, may be need in future *************/

                    onReceiveMethod.invoke(rec, appContext, intent);
                }
            }
        }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    }
}

j'espère qu'il pourra vous aider.

Note: le paramètre Context doit être un contexte D'Application comme le nom du paramètre l'indique, vous pouvez utiliser votre propre instance d'Application implémentée qui étend L'Application.

19
répondu xjy2061 2014-03-05 02:10:45

j'ai apporté quelques modifications à la réponse de @xjy2061.

les modifications sont:

  1. getdclaredfield to getField --> vous utilisez ceci si vous avez déclaré votre propre classe d'application. Sinon, il ne la trouvera pas.

aussi, n'oubliez pas de changer " com.votre.l'application" à votre propre classe de l'application canonique nom.

private static boolean setKitKatWebViewProxy(WebView webView, String host, int port) {
    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("acr.browser.barebones.Jerky");
        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);

                    /*********** optional, may be need in future *************/
                    final String CLASS_NAME = "android.net.ProxyProperties";
                    Class cls = Class.forName(CLASS_NAME);
                    Constructor constructor = cls.getConstructor(String.class, Integer.TYPE, String.class);
                    constructor.setAccessible(true);
                    Object proxyProperties = constructor.newInstance(host, port, null);
                    intent.putExtra("proxy", (Parcelable) proxyProperties);
                    /*********** optional, may be need in future *************/

                    onReceiveMethod.invoke(rec, appContext, intent);
                }
            }
        }
        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);
    } catch (InstantiationException 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;
}
4
répondu nubela 2014-01-23 12:44:29

je crée une application Android cordova, et je n'ai pas pu comprendre pourquoi les requêtes ajax vers des hôtes internes sur le réseau de ma société échouaient sur KitKat. Toutes les requêtes web natives ont réussi, et toutes les requêtes ajax sur les versions android ci-dessous 4.4 ont aussi réussi. Les requêtes ajax ont seulement échoué sur la société Interne wifi qui était encore plus perplexe.

S'avère KitKat utilise un nouveau chrome webview qui est différent des webviews standard utilisés dans versions précédentes d'android. Il y a un bug dans la version de chromium que kitkat utilise où il ne respecte pas la liste d'exclusion par procuration. Notre société wifi établit un serveur proxy, et et exclut tous les hôtes internes. Les requêtes ajax ont finalement échoué parce que l'authentification au proxy était défaillante. Puisque ces requêtes sont à des hôtes internes, il n'aurait jamais dû passer par le mandataire pour commencer. J'ai pu adapter la réponse de xjy2061 à mon usecase.

espérons que cela aide quelqu'un dans le futur et leur sauve quelques jours de cognement de la tête.

//Set KitKat proxy w/ proxy exclusion.    
@TargetApi(Build.VERSION_CODES.KITKAT)
public static void setKitKatWebViewProxy(Context appContext, String host, int port, String exclusionList) {

    Properties properties = System.getProperties();
    properties.setProperty("http.proxyHost", host);
    properties.setProperty("http.proxyPort", port + "");
    properties.setProperty("https.proxyHost", host);
    properties.setProperty("https.proxyPort", port + "");
    properties.setProperty("http.nonProxyHosts", exclusionList);
    properties.setProperty("https.nonProxyHosts", exclusionList);

    try {
        Class applictionCls = Class.forName("android.app.Application");
        Field loadedApkField = applictionCls.getDeclaredField("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);

                    /*********** optional, may be need in future *************/
                    final String CLASS_NAME = "android.net.ProxyProperties";
                    Class cls = Class.forName(CLASS_NAME);
                    Constructor constructor = cls.getConstructor(String.class, Integer.TYPE, String.class);
                    constructor.setAccessible(true);
                    Object proxyProperties = constructor.newInstance(host, port, exclusionList);
                    intent.putExtra("proxy", (Parcelable) proxyProperties);
                    /*********** optional, may be need in future *************/

                    onReceiveMethod.invoke(rec, appContext, intent);
                }
            }
        }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    }
}

vous appelleriez la méthode ci-dessus comme suit:

d'Abord importer cette bibliothèque en haut de votre fichier.

import android.util.ArrayMap;

puis appelez la méthode

int currentapiVersion = android.os.Build.VERSION.SDK_INT;
//check first to see if we are running KitKat
if (currentapiVersion >= Build.VERSION_CODES.KITKAT){
    setKitKatWebViewProxy(context, proxy, port, exclusionList);
}
2
répondu njtman 2014-11-06 14:22:15

https://android.googlesource.com/platform/external/chromium/+/android-4.4_r1/net/proxy/proxy_config_service_android.cc

a des méthodes pour définir le proxy. Je suis toujours en train d'essayer de comprendre comment invoquer cela à partir du code Java. Les pointeurs?

1
répondu Karthik 2013-11-24 05:37:10

https://codereview.chromium.org/26763005

Devinez à partir de ce patch, vous serez en mesure de mettre en place un proxy à nouveau dans un proche avenir, peut-être.

0
répondu user3105644 2013-12-16 00:07:13

a eu quelques problèmes avec la solution fournie sur certains périphériques lors du chargement de la page d'onCreate immédiatement après avoir paramétré la configuration de proxy. L'ouverture de la page Web après un léger retard a résolu le problème. On dirait que la configuration par procuration a besoin de temps pour être efficace.

0
répondu fabe 2014-06-26 12:34:06