美文网首页
[URL系列一]URL类源码解读

[URL系列一]URL类源码解读

作者: 高振华 | 来源:发表于2015-09-04 21:35 被阅读0次

    [URL系列一]URL类源码解读


    1.对该类的理解

    1.1 何为URL?(http://gaozh:pwd@www.gaozhenhua.cn:80/test1/test2/?a=3&b=4#reftest)

    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;
        }
    }
    

    相关文章

      网友评论

          本文标题:[URL系列一]URL类源码解读

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