HttpURLConnection的实现(原理基本层)

  在java进行网络请求的时候,使用的基本单位请求工具,就是最常用的

HttpURLConnection,有人是这是java标准库提供的基本小部件(whatever)。在进行高性能,高可用性选型的时候,很有必要,对HttpURLConnection最更加底层的了解。

        首先,在使用层面,无论使用什么网络框架,对于缓存,请求超时配置,请求体数据体的配置,https证书信任,等层面的东西,在工具框架库的层面,大同小异,只是封装的模式,提供的功能差异,不够根本的差距。

深层次一点,在进行socket请求时,请求的重试,重定向,socket连接池,协议的支持等层面,会更深一层次反应,这个框架基础的性能。是的,这里,我们仍然都是在sokect的基础上的。

*******************************下面,剖析HttpURLConnection的本来面目******************

        这个类的使用,这里不说了,网上到处都是,怎么做数据的设置提交,和数据的接收和关闭等等。

首先 HttpURLConnection 和 HttpsURLConnection 都是抽象类。

[java]view plaincopy

abstract public class HttpURLConnection extends URLConnection {}  

abstract public class HttpsURLConnection extends HttpURLConnection{}  

HttpURLConnection,和HttpsURLConnection只是做了http状态码的定义,默认head定义,和ssl工厂,hostName审核器的判断定义。真真正正的connect() 的实现,都是没有的。也就是说,这两个类抽象的部分,就是网络请求的根本部分。这样确实有解耦的作用。

通过源码,我们找到,HttpURLConnection实例对象的获得。

[java]view plaincopy

URLStreamHandler handler;  

public URLConnection openConnection() throws java.io.IOException {  

return handler.openConnection(mUrl);  

    }  

//这个静态方法,是获得对URL流处理器的根本,这里面,实现了URLStreamHandler的单例化,运行时只需要获得一次  

static URLStreamHandler getURLStreamHandler(String protocol) {  

//这里面,有一个工厂生成,这个工厂,可以通过外部实现接口,  

handler = factory.createURLStreamHandler(protocol);  

}  

这个用来生成URLStreamHandler的工程可以通过setURLStreamHandlerFactory设置。但是我们默认情况下,都是采用标准库的自己的实现。抽象类 URLStreamHandler 是所有流协议处理程序的通用超类.

这里才是整片文档的关键,

[java]view plaincopy

if (handler == null) {  

final String packagePrefixList = System.getProperty(protocolPathProp,"");  

StringTokenizer packagePrefixIter =new StringTokenizer(packagePrefixList, "|");  

while (handler == null && packagePrefixIter.hasMoreTokens()) {  

        String packagePrefix = packagePrefixIter.nextToken().trim();  

try {  

String clsName = packagePrefix +"." + protocol +  

".Handler";  

Class cls =null;  

try {  

                ClassLoader cl = ClassLoader.getSystemClassLoader();  

cls = Class.forName(clsName,true, cl);  

}catch (Exception ignored) {  

"white-space:pre">          //删除了一些异常处理,这里看主体。代码在URL.java中。  

        }  

    }  

}  

从代码看出来,jdk把具体的实现,交给了虚拟机的运行时。

[java]view plaincopy

java.protocol.handler.pkgs,这个虚拟机环境变量。这个值,可以通过命令改变,从而改变网络协议的具体实现。  

    java.protocol.handler.pkgs=com.acme.protocol  

    java.protocol.handler.pkgs=com.acme.protocol|com.acme.protocol2  

通过上面的代码块可以看出来,会默认优先加载前面的可加载类  

    类的命名模式为 [package_path].[protocol].Handler  

而可用来实现handler的类名称数据,是在编译是的一个类路径下。我们可以根据工厂有自己的协议实现,也可以通过这种热加载机制,在虚拟器启动时,改变虚拟机java.protocol.handler.pkg这个环境变量key的值。

如果根据这个环境变量,还是没有能够加载URLStreamHandler的类,jdk1.8默认有几个固定的判断。

[java]view plaincopy

if (handler == null) {  

try {  

// BEGIN Android-changed  

// Use of okhttp for http and https  

// Removed unnecessary use of reflection for sun classes  

if (protocol.equals("file")) {  

handler =new sun.net.www.protocol.file.Handler();  

}else if (protocol.equals("ftp")) {  

handler =new sun.net.www.protocol.ftp.Handler();  

}else if (protocol.equals("jar")) {  

handler =new sun.net.www.protocol.jar.Handler();  

}else if (protocol.equals("http")) {  

            handler = (URLStreamHandler)Class.  

forName("com.android.okhttp.HttpHandler").newInstance();  

}else if (protocol.equals("https")) {  

            handler = (URLStreamHandler)Class.  

forName("com.android.okhttp.HttpsHandler").newInstance();  

        }  

// END Android-changed  

}catch (Exception e) {  

throw new AssertionError(e);  

    }  

}  

这里有关于这种机制的讨论:https://accu.org/index.php/journals/1434

这里,找到两个实现,可以看一下:

1>.HttpConnection对于网络的实现,是默认外包的,外包方大多取决于定制的平台,大多还是sun自己httpClient的子孙或者嫡系,这个我没有找到关于这个的实现。

在rt.jar包中有一种实现(基本上可以把这个作为大多数情况下jdk的实现)。sun.net.www.protocol.https.HttpsURLConnectionImpl(DelegateHttpsURLConnection):

[java]view plaincopy

sun.net.www.protocol.http.HttpURLConnection extends java.net.HttpURLConnection{  

public void connect() throws IOException {  

synchronized(this) {  

this.connecting = true;  

        }  

this.plainConnect();//这里首先会进行 URLtoSocketPermission 检查(只是协议主机路径等的合法性)  

    }  

}  

上面可以看到sun.net.www.protocol里有一套具体的请求实现,

[java]view plaincopy

plainConnect0();//此方法首先会使用cacheHandler设置cachedResponse,作为部分字段。  

//然后这个方法包含走代理的逻辑。instProxy作为代理。  

//最后无论是否会使用代理或者代理的层级,都会动过HttpClient.New()方法使用HttpClient.  

通过调用getInputStream()->getInputStream0也会间接地调用getOutputStream(),而调用getOutputStream()则会间接使用getOutputStream0()使用HttpClient  http.getOutputStream()方法。最终的请求都是HttpClient的父类NetworkClient所做,根据传递的信息配置和使用 InetSocketAddress。

    2>.下面是OKHttp的实现,这个就不用多说了,利用Okhttp框架的源码部分,新版本系统已经作为默认的实现:

[java]view plaincopy

public final class HttpHandler extends URLStreamHandler {  

@Override protected URLConnection openConnection(URL url) throws IOException {  

return new OkHttpClient().open(url);  

    }  

@Override protected URLConnection openConnection(URL url, Proxy proxy) throws IOException {  

if (url == null || proxy == null) {  

throw new IllegalArgumentException("url == null || proxy == null");  

        }  

//‘com.squareup.okhttp:okhttp:1.5.0’  

return new OkHttpClient().setProxy(proxy).open(url);//开放出来的Builder类并没有open方法。  

    }  

@Override protected int getDefaultPort() {  

return 80;  

    }  

}  

[java]view plaincopy

//上面对网络请求,会发到excute上。。。后面的东西就是OKHttp的部分了。  

execute(boolean readResponse) throws IOException {  

// 调用了HttpEngine的sendRequest方法。  

  httpEngine.sendRequest();  

  route = httpEngine.getRoute();  

handshake = httpEngine.getConnection() !=null ? httpEngine.getConnection().getHandshake(): null;  

}  

整体就是:HttpURLConnection的抽象方法connect等  <--需要URLStreamHandler来产生 <--handler需要自定义的工厂创建或者jdk自己实现的

网上,别人的文档,来源未知:

Android 2.2版本之前,bug比如说对一个可读的InputStream调用close()方法时,就有可能会导致连接池失效了。那么我们通常的解决办法就是直接禁用掉连接池的功能:

在Android 4.0版本中,我们又添加了一些响应的缓存机制。当缓存被安装后(调用HttpResponseCache的install()方法),所有的HTTP请求都会满足以下三种情况:

比较轻便,灵活,易于扩展

在3.0后以及4.0中都进行了改善,如对HTTPS的支持

在4.0中,还增加了对缓存的支持

在android 2.2及以下版本中HttpUrlConnection存在着一些bug,所以建议在android 2.3以后使用HttpUrlConnection,2.3之前使用HttpClient。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,904评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,581评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,527评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,463评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,546评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,572评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,582评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,330评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,776评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,087评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,257评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,923评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,571评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,192评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,436评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,145评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容