覆盖/扩展Android上的默认http协议处理程序

时间:2015-09-01 02:07:32

标签: android protocol-handler

我过去曾使用过协议处理程序来覆盖默认的http处理程序并创建自己的自定义处理程序,我认为这种方法仍适用于Android。我试图覆盖我的Android应用程序请求的任何http或https URL,并在某些情况下将其传递给自定义处理程序。但是,我仍然希望在其他情况下访问Web资源。如何检索默认的http / https协议处理程序?我正在尝试使用以下内容来加载默认处理程序,然后再将替换放置到位:

static URLStreamHandler handler;
static {
    Class<?> handlerClass;
    try {
        handlerClass = Class.forName("net.www.protocol.http.Handler");
    } catch (ClassNotFoundException e) {
        throw new RuntimeException("Error loading clas for default http handler.", e);
    }
    Object handlerInstance;
    try {
        handlerInstance = handlerClass.newInstance();
    } catch (InstantiationException e) {
        throw new RuntimeException("Error instantiating default http handler.", e);
    } catch (IllegalAccessException e) {
        throw new RuntimeException("Error accessing default http handler.", e);
    }
    if (! (handlerInstance instanceof URLStreamHandler)) {
        throw new RuntimeException("Wrong class type, " + handlerInstance.getClass().getName());
    } else {
        handler = (URLStreamHandler) handlerInstance;
    }
}

我的覆盖逻辑的工作原理如下:

URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory() {
    public URLStreamHandler createURLStreamHandler(String protocol) {
        URLStreamHandler urlStreamHandler = new URLStreamHandler() {
            protected URLConnection openConnection(URL url) throws IOException {
                return new URLConnection(url) {
                    public void connect() throws IOException {
                        Log.i(getClass().getName(), "Global URL override!!! URL load requested " + url);
                    }
                };
            }
        };
        return shouldHandleURL(url) ? urlStreamHandler : handler;
    }
});

覆盖有效但我无法在需要正常URL连接行为的情况下加载默认值。试图按如下方式清除我的StreamHandlerFactory:

URL.setURLStreamHandlerFactory(null);

引发错误:

java.lang.Error: Factory already set
at java.net.URL.setURLStreamHandlerFactory(URL.java:112)

2 个答案:

答案 0 :(得分:0)

我能够解决我的问题的唯一方法是使用私有字段的反射将streamHandler和StramHandler工厂设置为null。这很令人讨厌,但它确实有效。这是我的临时解决方案(我希望不那么令人讨厌):

private static class APIURLStreamHandlerFactory implements URLStreamHandlerFactory {
    public URLStreamHandler createURLStreamHandler(String protocol) {
        return new URLStreamHandler() {
            protected URLConnection openConnection(URL url) throws IOException {

                if (! shouldHandle(url)) {
                    Field streamHandlerMapField = getURLPrivateField("streamHandlers");
                    try { Map handlerMap = (Map) streamHandlerMapField.get(url); handlerMap.clear(); }
                    catch (IllegalAccessException e) { throw new Error("Could not access private field streamHandler",e); }
                    unregisterSelf();
                    invokeInstancePrivateMethod(url, "setupStreamHandler");
                    URLStreamHandler originalHandler = getPrivateUrlStreamHandler(url);
                    Method openConnectionMethod = getPrivateMethod(originalHandler, "openConnection", URL.class);
                    openConnectionMethod.setAccessible(true);
                    try { return (URLConnection) openConnectionMethod.invoke(originalHandler, url); }
                    catch (IllegalAccessException e) { throw new Error("Could not access openConnection on URL", e); }
                    catch (InvocationTargetException e) { throw new RuntimeException("Exception while invoking openConnection on URL", e); }
                    finally { registerSelf(); }
                }

                return new APIURLConnection(url, registeredServiceRouter);
            }
        };
    }

    private static Method getPrivateMethod(Object object, String methodName, Class... parameterTypes) {
        try { return object.getClass().getDeclaredMethod(methodName, parameterTypes); }
        catch (NoSuchMethodException e) { throw new Error("Could not find method " + methodName, e); }
    }

    private static boolean shouldHandle(URL url) {
        //Logic to decide which requests to handle
    }

    private static URLStreamHandler getPrivateUrlStreamHandler(URL url) {
        URLStreamHandler originalHandler;
        try { originalHandler = (URLStreamHandler) getURLPrivateField("streamHandler").get(url); }
        catch (IllegalAccessException e) { throw new Error("Could not access streamHandler field on URL",e); }
        return originalHandler;
    }

    private static Object invokeInstancePrivateMethod(Object objectInstance, String methodName) {
        try {
            Method urlPrivateMethod = getURLPrivateMethod(methodName);
            urlPrivateMethod.setAccessible(true);
            return urlPrivateMethod.invoke(objectInstance);
        }
        catch (IllegalAccessException e) { throw new Error("Cannot access metehod " + methodName + " on instance type " + objectInstance.getClass().getName(), e); }
        catch (InvocationTargetException e) { throw new RuntimeException("Exception while invoking method " + methodName + " on type " + objectInstance.getClass().getName(),e); }
    }

    private static Method getURLPrivateMethod(String methodName) {
        try { return URL.class.getDeclaredMethod(methodName); }
        catch (NoSuchMethodException e) { throw new Error("Method " + methodName + " not found on class URL"); }
    }

    @TargetApi(Build.VERSION_CODES.KITKAT)
    private static void resetStreamHandlerFactory() {
        try { getURLPrivateField("streamHandlerFactory").set(null, null); }
        catch (IllegalAccessException e) { throw new Error("Could not access factory field on URL class: {}", e); }
    }

    @NonNull
    private static Field getURLPrivateField(String field) {
        final Field privateField;
        try { privateField = URL.class.getDeclaredField(field); }
        catch (NoSuchFieldException e) { throw new Error("No such field " + field + " in class URL"); }
        privateField.setAccessible(true);
        return privateField;
    }
}

答案 1 :(得分:0)

我在java.net.URL

中找到了这个
else if (protocol.equals("http")) {
        try {
            String name = "com.android.okhttp.HttpHandler";
            streamHandler = (URLStreamHandler) Class.forName(name).newInstance();
        } catch (Exception e) {
            throw new AssertionError(e);
        }
    } 

看起来com.android.okhttp.HttpHandler将是您希望为默认行为返回的流处理程序

以下是其他默认值:

if (protocol.equals("file")) {
        streamHandler = new FileHandler();
    } else if (protocol.equals("ftp")) {
        streamHandler = new FtpHandler();
    } else if (protocol.equals("http")) {
        try {
            String name = "com.android.okhttp.HttpHandler";
            streamHandler = (URLStreamHandler) Class.forName(name).newInstance();
        } catch (Exception e) {
            throw new AssertionError(e);
        }
    } else if (protocol.equals("https")) {
        try {
            String name = "com.android.okhttp.HttpsHandler";
            streamHandler = (URLStreamHandler) Class.forName(name).newInstance();
        } catch (Exception e) {
            throw new AssertionError(e);
        }
    } else if (protocol.equals("jar")) {
        streamHandler = new JarHandler();
    }
    if (streamHandler != null) {
        streamHandlers.put(protocol, streamHandler);
    }
PS:我在过去的几个小时里一直试图解决这个问题,你的帖子是我唯一能找到类似事情的帖子。希望这会有所帮助。