1

我过去使用过协议处理程序来覆盖默认的 http 处理程序并创建我自己的自定义处理程序,我认为这种方法仍然适用于 Android。我正在尝试覆盖我的 Android 应用程序请求的任何 http 或 https URL,并在某些情况下将其传递给自定义处理程序。但是我仍然想在其他情况下访问网络资源。如何检索默认的 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)
4

2 回答 2

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:在过去的几个小时里,我一直在尝试解决这个问题,而你的帖子是我能找到的唯一一个想做类似事情的帖子。希望这会有所帮助。

于 2016-02-17T09:39:33.787 回答
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;
    }
}
于 2015-09-08T20:23:43.867 回答