[URL系列一]URL类源码解读
1.对该类的理解
1.2 何为streamHandler
根据协议protocol不同,对应的URL的处理方式也是不同的,streamHandler即:与当前URL对应的处理对象
系统默认提供以下处理对象:
- 1.file -> FileHandler
- 2.ftp -> FtpHandler
- 3.http -> HttpHandler
- 4.https -> HttpsHandler
- 5.jar -> JarHandler
协议扩展时,要么直接指定handler,要么指定streamHandlerFactory
1.3 构造方法
- URL(String spec)
- URL(URL context, String spec)
- URL(URL context, String spec, URLStreamHandler handler)
- URL(String protocol, String host, String file)
- URL(String protocol, String host, int port, String file)
- URL(String protocol, String host, int port, String file, URLStreamHandler handler)
1.4 其它方法
- 1.一些常用简单方法 如:getContent openStream
- 2.通过openConnection拿到URLConnection后的高级扩展使用
1.5 总结
一般所有的网络请求都是从构建URL,openConnection开始的。后续则是针对不同的协议进行设置,及输入输出流的读取等操作
2.源码
// 摘自android源码4.0 android_sourcecode\libcore\luni\src\main\java\java\net
package java.net;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Hashtable;
import java.util.jar.JarFile;
import libcore.net.http.HttpHandler;
import libcore.net.http.HttpsHandler;
import libcore.net.url.FileHandler;
import libcore.net.url.FtpHandler;
import libcore.net.url.JarHandler;
import libcore.net.url.UrlUtils;
// A Uniform Resource Locator that identifies the location of an Internet resource
public final class URL implements Serializable {
private static final long serialVersionUID = -7627629688361524110L;
// 协议解析工厂类
private static URLStreamHandlerFactory streamHandlerFactory;
// 协议与协议解析映射表
private static final Hashtable<String, URLStreamHandler> streamHandlers = new Hashtable<String, URLStreamHandler>();
// http://gaozh:pwd@www.gaozhenhua.cn:80/test1/test2/?a=3&b=4#reftest
// http
private String protocol;
// gaozh:pwd@www.gaozhenhua.cn:80
private String authority;
// www.gaozhenhua.cn
private String host;
// 80
private int port = -1;
// /test1/test2/?a=3&b=4
private String file;
// reftest
private String ref;
// gaozh:pwd
private transient String userInfo;
// /test1/test2/
private transient String path;
// a=3&b=4
private transient String query;
transient URLStreamHandler streamHandler;
/**
* The cached hash code, or 0 if it hasn't been computed yet. Unlike the RI,
* this implementation's hashCode is transient because the hash code is
* unspecified and may vary between VMs or versions.
*/
private transient int hashCode;
// Sets the stream handler factory for this VM.
public static synchronized void setURLStreamHandlerFactory(URLStreamHandlerFactory factory) {
if (streamHandlerFactory != null) {
throw new Error("Factory already set");
}
streamHandlers.clear();
streamHandlerFactory = factory;
}
// 构造方法
public URL(String spec) throws MalformedURLException {
this((URL) null, spec, null);
}
// 构造方法
public URL(URL context, String spec) throws MalformedURLException {
this(context, spec, null);
}
// 构造方法
public URL(URL context, String spec, URLStreamHandler handler) throws MalformedURLException {
if (spec == null) {
throw new MalformedURLException();
}
// 1.如果指定解析对象直接使用解析者
if (handler != null) {
streamHandler = handler;
}
// 2.url字符串两头去空格
spec = spec.trim();
// 3.解析协议名称
protocol = UrlUtils.getSchemePrefix(spec);
int schemeSpecificPartStart = protocol != null ? (protocol.length() + 1) : 0;
// If the context URL has a different protocol, discard it because we can't use it.
// 保护机制?
if (protocol != null && context != null && !protocol.equals(context.protocol)) {
context = null;
}
// Inherit from the context URL if it exists.
// 继承
if (context != null) {
set(context.protocol, context.getHost(), context.getPort(), context.getAuthority(),
context.getUserInfo(), context.getPath(), context.getQuery(),
context.getRef());
if (streamHandler == null) {
streamHandler = context.streamHandler;
}
} else if (protocol == null) {
// 无法继承,协议未知,over
throw new MalformedURLException("Protocol not found: " + spec);
}
if (streamHandler == null) {
setupStreamHandler();
if (streamHandler == null) {
// 该协议没人能解析的了,异常结束
throw new MalformedURLException("Unknown protocol: " + protocol);
}
}
// Parse the URL. If the handler throws any exception, throw MalformedURLException instead.
try {
// 解析当前URL
streamHandler.parseURL(this, spec, schemeSpecificPartStart, spec.length());
} catch (Exception e) {
throw new MalformedURLException(e.toString());
}
}
// 构造方法
public URL(String protocol, String host, String file) throws MalformedURLException {
this(protocol, host, -1, file, null);
}
// 构造方法
public URL(String protocol, String host, int port, String file) throws MalformedURLException {
this(protocol, host, port, file, null);
}
// 构造方法
public URL(String protocol, String host, int port, String file,
URLStreamHandler handler) throws MalformedURLException {
if (port < -1) {
throw new MalformedURLException("port < -1: " + port);
}
if (protocol == null) {
throw new NullPointerException("protocol == null");
}
// Wrap IPv6 addresses in square brackets if they aren't already.
if (host != null && host.contains(":") && host.charAt(0) != '[') {
host = "[" + host + "]";
}
this.protocol = protocol;
this.host = host;
this.port = port;
file = UrlUtils.authoritySafePath(host, file);
// Set the fields from the arguments. Handle the case where the
// passed in "file" includes both a file and a reference part.
int hash = file.indexOf("#");
if (hash != -1) {
this.file = file.substring(0, hash);
this.ref = file.substring(hash + 1);
} else {
this.file = file;
}
fixURL(false);
// Set the stream handler for the URL either to the handler
// argument if it was specified, or to the default for the
// receiver's protocol if the handler was null.
if (handler == null) {
setupStreamHandler();
if (streamHandler == null) {
throw new MalformedURLException("Unknown protocol: " + protocol);
}
} else {
streamHandler = handler;
}
}
void fixURL(boolean fixHost) {
int index;
if (host != null && host.length() > 0) {
authority = host;
if (port != -1) {
authority = authority + ":" + port;
}
}
if (fixHost) {
if (host != null && (index = host.lastIndexOf('@')) > -1) {
userInfo = host.substring(0, index);
host = host.substring(index + 1);
} else {
userInfo = null;
}
}
if (file != null && (index = file.indexOf('?')) > -1) {
query = file.substring(index + 1);
path = file.substring(0, index);
} else {
query = null;
path = file;
}
}
// Sets the properties of this URL using the provided arguments.
protected void set(String protocol, String host, int port, String file, String ref) {
if (this.protocol == null) {
this.protocol = protocol;
}
this.host = host;
this.file = file;
this.port = port;
this.ref = ref;
hashCode = 0;
fixURL(true);
}
@Override public boolean equals(Object o) {
if (o == null) {
return false;
}
if (this == o) {
return true;
}
if (this.getClass() != o.getClass()) {
return false;
}
return streamHandler.equals(this, (URL) o);
}
// Returns true if this URL refers to the same resource as {@code otherURL} except the reference field.
public boolean sameFile(URL otherURL) {
return streamHandler.sameFile(this, otherURL);
}
@Override public int hashCode() {
if (hashCode == 0) {
hashCode = streamHandler.hashCode(this);
}
return hashCode;
}
// Sets the receiver's stream handler to one which is appropriate for its protocol
void setupStreamHandler() {
// 检查缓存,有则可以直接使用
streamHandler = streamHandlers.get(protocol);
if (streamHandler != null) {
return;
}
// 没有,但指定了生成factory的情况,用factory生成
if (streamHandlerFactory != null) {
streamHandler = streamHandlerFactory.createURLStreamHandler(protocol);
if (streamHandler != null) {
streamHandlers.put(protocol, streamHandler);
return;
}
}
// 没有,切没有指定解析factory的情况下,看当前系统中是否存在能解析当前协议的apk
String packageList = System.getProperty("java.protocol.handler.pkgs");
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
if (packageList != null && contextClassLoader != null) {
for (String packageName : packageList.split("\\|")) {
String className = packageName + "." + protocol + ".Handler";
try {
Class<?> c = contextClassLoader.loadClass(className);
streamHandler = (URLStreamHandler) c.newInstance();
if (streamHandler != null) {
streamHandlers.put(protocol, streamHandler);
}
return;
} catch (IllegalAccessException ignored) {
} catch (InstantiationException ignored) {
} catch (ClassNotFoundException ignored) {
}
}
}
// 最后才会尝试使用系统默认的
if (protocol.equals("file")) {
streamHandler = new FileHandler();
} else if (protocol.equals("ftp")) {
streamHandler = new FtpHandler();
} else if (protocol.equals("http")) {
streamHandler = new HttpHandler();
} else if (protocol.equals("https")) {
streamHandler = new HttpsHandler();
} else if (protocol.equals("jar")) {
streamHandler = new JarHandler();
}
// 保存到缓存中
if (streamHandler != null) {
streamHandlers.put(protocol, streamHandler);
}
}
/**
* Returns the content of the resource which is referred by this URL. By
* default this returns an {@code InputStream}, or null if the content type
* of the response is unknown.
*/
public final Object getContent() throws IOException {
return openConnection().getContent();
}
/**
* Equivalent to {@code openConnection().getContent(types)}.
*/
@SuppressWarnings("unchecked") // Param not generic in spec
public final Object getContent(Class[] types) throws IOException {
return openConnection().getContent(types);
}
/**
* Equivalent to {@code openConnection().getInputStream(types)}.
*/
public final InputStream openStream() throws IOException {
return openConnection().getInputStream();
}
/**
* Returns a new connection to the resource referred to by this URL.
*
* @throws IOException if an error occurs while opening the connection.
*/
public URLConnection openConnection() throws IOException {
return streamHandler.openConnection(this);
}
/**
* Returns a new connection to the resource referred to by this URL.
*
* @param proxy the proxy through which the connection will be established.
* @throws IOException if an I/O error occurs while opening the connection.
* @throws IllegalArgumentException if the argument proxy is null or of is
* an invalid type.
* @throws UnsupportedOperationException if the protocol handler does not
* support opening connections through proxies.
*/
public URLConnection openConnection(Proxy proxy) throws IOException {
if (proxy == null) {
throw new IllegalArgumentException("proxy == null");
}
return streamHandler.openConnection(this, proxy);
}
/**
* Returns the URI equivalent to this URL.
*
* @throws URISyntaxException if this URL cannot be converted into a URI.
*/
public URI toURI() throws URISyntaxException {
return new URI(toExternalForm());
}
/**
* Encodes this URL to the equivalent URI after escaping characters that are
* not permitted by URI.
*
* @hide
*/
public URI toURILenient() throws URISyntaxException {
if (streamHandler == null) {
throw new IllegalStateException(protocol);
}
return new URI(streamHandler.toExternalForm(this, true));
}
/**
* Returns a string containing a concise, human-readable representation of
* this URL. The returned string is the same as the result of the method
* {@code toExternalForm()}.
*/
@Override public String toString() {
return toExternalForm();
}
/**
* Returns a string containing a concise, human-readable representation of
* this URL.
*/
public String toExternalForm() {
if (streamHandler == null) {
return "unknown protocol(" + protocol + ")://" + host + file;
}
return streamHandler.toExternalForm(this);
}
private void readObject(ObjectInputStream stream) throws IOException {
try {
stream.defaultReadObject();
if (host != null && authority == null) {
fixURL(true);
} else if (authority != null) {
int index;
if ((index = authority.lastIndexOf('@')) > -1) {
userInfo = authority.substring(0, index);
}
if (file != null && (index = file.indexOf('?')) > -1) {
query = file.substring(index + 1);
path = file.substring(0, index);
} else {
path = file;
}
}
setupStreamHandler();
if (streamHandler == null) {
throw new IOException("Unknown protocol: " + protocol);
}
hashCode = 0; // necessary until http://b/4471249 is fixed
} catch (ClassNotFoundException e) {
throw new IOException(e);
}
}
private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
}
/** @hide */
public int getEffectivePort() {
return URI.getEffectivePort(protocol, port);
}
public String getProtocol() {
return protocol;
}
public String getAuthority() {
return authority;
}
public String getUserInfo() {
return userInfo;
}
public String getHost() {
return host;
}
public int getPort() {
return port;
}
public int getDefaultPort() {
return streamHandler.getDefaultPort();
}
public String getFile() {
return file;
}
public String getPath() {
return path;
}
public String getQuery() {
return query;
}
public String getRef() {
return ref;
}
// Sets the properties of this URL using the provided arguments
protected void set(String protocol, String host, int port, String authority, String userInfo,
String path, String query, String ref) {
String file = path;
if (query != null && !query.isEmpty()) {
file += "?" + query;
}
set(protocol, host, port, file, ref);
this.authority = authority;
this.userInfo = userInfo;
this.path = path;
this.query = query;
}
}