美文网首页Android 开发工具
通过webView设置代理实现离线访问

通过webView设置代理实现离线访问

作者: galafan | 来源:发表于2019-04-04 13:28 被阅读0次

    最近项目遇到一个需求,要在app没有网络的状态下去访问h5,实现app的离线访问,废话不多讲直接上实现原理通过给webView设置代理(Proxy)的方式将webView所有访问地址代理到127.0.01的本地服务上,原理图

    image

    关键1:给webView设置代理

    IProxyCallback.aidl 和 IProxyPortListener.aidl 文件;

    创建 IProxyCallback.aidl如下:

    interfaceIProxyCallback
    {
    oneway voidgetProxyPort(IBinder callback);
    }
    

    创建IProxyPortListener.aidl 如下:

    interfaceIProxyPortListener
     
    {
     oneway voidsetProxyPort(intport);
     }
    

    设置代理

    package com.mobisoft.mbswebplugin.proxy.server;
    
    import android.annotation.SuppressLint;
    import android.content.Context;
    import android.content.Intent;
    import android.net.Proxy;
    import android.os.Build;
    import android.util.ArrayMap;
    import android.util.Log;
    
    import com.tencent.smtt.sdk.WebView;
    
    import java.io.PrintWriter;
    import java.io.StringWriter;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.Properties;
    
    /**
     * Author:Created by fan.xd on 2017/1/18.
     * Email:fang.xd@mobisoft.com.cn
     * Description:设置代理
     */
    
    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);
                return false;
            }
            // 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("com.tencent.smtt.sdk.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("com.tencent.smtt.sdk.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("com.tencent.smtt.sdk.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("com.tencent.smtt.sdk.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."+ applicationClassName);
    
            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);
                            intent.putExtra("proxyInfo","ssss");
    
                            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;
        }
    
    }
    

    参考:给 WebView 设置代理

    相关文章

      网友评论

        本文标题:通过webView设置代理实现离线访问

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