我过去曾使用过协议处理程序来覆盖默认的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)
答案 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:我在过去的几个小时里一直试图解决这个问题,你的帖子是我唯一能找到类似事情的帖子。希望这会有所帮助。