HttpURLConnection的實(shí)現(xiàn)(原理基本層)

? 在java進(jìn)行網(wǎng)絡(luò)請(qǐng)求的時(shí)候毙芜,使用的基本單位請(qǐng)求工具,就是最常用的

HttpURLConnection争拐,有人是這是java標(biāo)準(zhǔn)庫(kù)提供的基本小部件(whatever)腋粥。在進(jìn)行高性能晦雨,高可用性選型的時(shí)候,很有必要隘冲,對(duì)HttpURLConnection最更加底層的了解闹瞧。

? ? ? ? 首先,在使用層面展辞,無論使用什么網(wǎng)絡(luò)框架奥邮,對(duì)于緩存,請(qǐng)求超時(shí)配置罗珍,請(qǐng)求體數(shù)據(jù)體的配置洽腺,https證書信任,等層面的東西覆旱,在工具框架庫(kù)的層面蘸朋,大同小異,只是封裝的模式扣唱,提供的功能差異藕坯,不夠根本的差距。

深層次一點(diǎn)噪沙,在進(jìn)行socket請(qǐng)求時(shí)炼彪,請(qǐng)求的重試,重定向正歼,socket連接池辐马,協(xié)議的支持等層面,會(huì)更深一層次反應(yīng)朋腋,這個(gè)框架基礎(chǔ)的性能齐疙。是的,這里旭咽,我們?nèi)匀欢际窃趕okect的基礎(chǔ)上的贞奋。

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

? ? ? ? 這個(gè)類的使用穷绵,這里不說了轿塔,網(wǎng)上到處都是,怎么做數(shù)據(jù)的設(shè)置提交仲墨,和數(shù)據(jù)的接收和關(guān)閉等等勾缭。

首先 HttpURLConnection 和 HttpsURLConnection 都是抽象類。

[java]view plaincopy

abstract?public?class?HttpURLConnection?extends?URLConnection?{}??

abstract?public?class?HttpsURLConnection?extends?HttpURLConnection{}??

HttpURLConnection,和HttpsURLConnection只是做了http狀態(tài)碼的定義目养,默認(rèn)head定義俩由,和ssl工廠,hostName審核器的判斷定義癌蚁。真真正正的connect()?的實(shí)現(xiàn)幻梯,都是沒有的兜畸。也就是說,這兩個(gè)類抽象的部分碘梢,就是網(wǎng)絡(luò)請(qǐng)求的根本部分咬摇。這樣確實(shí)有解耦的作用。

通過源碼煞躬,我們找到肛鹏,HttpURLConnection實(shí)例對(duì)象的獲得。

[java]view plaincopy

URLStreamHandler?handler;??

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

return?handler.openConnection(mUrl);??

????}??

//這個(gè)靜態(tài)方法恩沛,是獲得對(duì)URL流處理器的根本在扰,這里面,實(shí)現(xiàn)了URLStreamHandler的單例化复唤,運(yùn)行時(shí)只需要獲得一次??

static?URLStreamHandler?getURLStreamHandler(String?protocol)?{??

//這里面健田,有一個(gè)工廠生成,這個(gè)工廠佛纫,可以通過外部實(shí)現(xiàn)接口,??

handler?=?factory.createURLStreamHandler(protocol);??

}??

這個(gè)用來生成URLStreamHandler的工程可以通過setURLStreamHandlerFactory設(shè)置总放。但是我們默認(rèn)情況下呈宇,都是采用標(biāo)準(zhǔn)庫(kù)的自己的實(shí)現(xiàn)。抽象類 URLStreamHandler 是所有流協(xié)議處理程序的通用超類.

這里才是整片文檔的關(guān)鍵局雄,

[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把具體的實(shí)現(xiàn),交給了虛擬機(jī)的運(yùn)行時(shí)宫盔。

[java]view plaincopy

java.protocol.handler.pkgs融虽,這個(gè)虛擬機(jī)環(huán)境變量。這個(gè)值灼芭,可以通過命令改變有额,從而改變網(wǎng)絡(luò)協(xié)議的具體實(shí)現(xiàn)。??

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

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

通過上面的代碼塊可以看出來彼绷,會(huì)默認(rèn)優(yōu)先加載前面的可加載類??

????類的命名模式為?[package_path].[protocol].Handler??

而可用來實(shí)現(xiàn)handler的類名稱數(shù)據(jù)巍佑,是在編譯是的一個(gè)類路徑下。我們可以根據(jù)工廠有自己的協(xié)議實(shí)現(xiàn)寄悯,也可以通過這種熱加載機(jī)制萤衰,在虛擬器啟動(dòng)時(shí),改變虛擬機(jī)java.protocol.handler.pkg這個(gè)環(huán)境變量key的值猜旬。

如果根據(jù)這個(gè)環(huán)境變量脆栋,還是沒有能夠加載URLStreamHandler的類倦卖,jdk1.8默認(rèn)有幾個(gè)固定的判斷。

[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);??

????}??

}??

這里有關(guān)于這種機(jī)制的討論:https://accu.org/index.php/journals/1434

這里筹吐,找到兩個(gè)實(shí)現(xiàn)糖耸,可以看一下:

1>.HttpConnection對(duì)于網(wǎng)絡(luò)的實(shí)現(xiàn),是默認(rèn)外包的丘薛,外包方大多取決于定制的平臺(tái)嘉竟,大多還是sun自己httpClient的子孫或者嫡系,這個(gè)我沒有找到關(guān)于這個(gè)的實(shí)現(xiàn)洋侨。

在rt.jar包中有一種實(shí)現(xiàn)(基本上可以把這個(gè)作為大多數(shù)情況下jdk的實(shí)現(xiàn))舍扰。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();//這里首先會(huì)進(jìn)行?URLtoSocketPermission?檢查(只是協(xié)議主機(jī)路徑等的合法性)??

????}??

}??

上面可以看到sun.net.www.protocol里有一套具體的請(qǐng)求實(shí)現(xiàn),

[java]view plaincopy

plainConnect0();//此方法首先會(huì)使用cacheHandler設(shè)置cachedResponse,作為部分字段希坚。??

//然后這個(gè)方法包含走代理的邏輯边苹。instProxy作為代理。??

//最后無論是否會(huì)使用代理或者代理的層級(jí)裁僧,都會(huì)動(dòng)過HttpClient.New()方法使用HttpClient.??

通過調(diào)用getInputStream()->getInputStream0也會(huì)間接地調(diào)用getOutputStream()个束,而調(diào)用getOutputStream()則會(huì)間接使用getOutputStream0()使用HttpClient? http.getOutputStream()方法。最終的請(qǐng)求都是HttpClient的父類NetworkClient所做聊疲,根據(jù)傳遞的信息配置和使用?InetSocketAddress茬底。

? ? 2>.下面是OKHttp的實(shí)現(xiàn),這個(gè)就不用多說了获洲,利用Okhttp框架的源碼部分阱表,新版本系統(tǒng)已經(jīng)作為默認(rèn)的實(shí)現(xiàn):

[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

//上面對(duì)網(wǎng)絡(luò)請(qǐng)求贡珊,會(huì)發(fā)到excute上最爬。。门岔。后面的東西就是OKHttp的部分了爱致。??

execute(boolean?readResponse)?throws?IOException?{??

//?調(diào)用了HttpEngine的sendRequest方法。??

??httpEngine.sendRequest();??

??route?=?httpEngine.getRoute();??

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

}??

整體就是:HttpURLConnection的抽象方法connect等? <--需要URLStreamHandler來產(chǎn)生 <--handler需要自定義的工廠創(chuàng)建或者jdk自己實(shí)現(xiàn)的

網(wǎng)上固歪,別人的文檔蒜鸡,來源未知:

Android 2.2版本之前,bug比如說對(duì)一個(gè)可讀的InputStream調(diào)用close()方法時(shí)牢裳,就有可能會(huì)導(dǎo)致連接池失效了逢防。那么我們通常的解決辦法就是直接禁用掉連接池的功能:

在Android 4.0版本中,我們又添加了一些響應(yīng)的緩存機(jī)制蒲讯。當(dāng)緩存被安裝后(調(diào)用HttpResponseCache的install()方法)忘朝,所有的HTTP請(qǐng)求都會(huì)滿足以下三種情況:

比較輕便,靈活判帮,易于擴(kuò)展

在3.0后以及4.0中都進(jìn)行了改善局嘁,如對(duì)HTTPS的支持

在4.0中溉箕,還增加了對(duì)緩存的支持

在android 2.2及以下版本中HttpUrlConnection存在著一些bug,所以建議在android 2.3以后使用HttpUrlConnection悦昵,2.3之前使用HttpClient肴茄。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市但指,隨后出現(xiàn)的幾起案子寡痰,更是在濱河造成了極大的恐慌,老刑警劉巖棋凳,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拦坠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡剩岳,警方通過查閱死者的電腦和手機(jī)贞滨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拍棕,“玉大人晓铆,你說我怎么就攤上這事〈虏ィ” “怎么了尤蒿?”我有些...
    開封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)幅垮。 經(jīng)常有香客問我,道長(zhǎng)尾组,這世上最難降的妖魔是什么忙芒? 我笑而不...
    開封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮讳侨,結(jié)果婚禮上呵萨,老公的妹妹穿的比我還像新娘。我一直安慰自己跨跨,他們只是感情好潮峦,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著勇婴,像睡著了一般忱嘹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上耕渴,一...
    開封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天柿冲,我揣著相機(jī)與錄音亿傅,去河邊找鬼。 笑死版保,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的亏镰。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼医寿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蘑斧,我...
    開封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤靖秩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后乌叶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盆偿,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年准浴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了事扭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡乐横,死狀恐怖求橄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情葡公,我是刑警寧澤罐农,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站催什,受9級(jí)特大地震影響涵亏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蒲凶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一气筋、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧旋圆,春花似錦宠默、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至刻肄,卻和暖如春瓤球,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背肄方。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工冰垄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓虹茶,卻偏偏與公主長(zhǎng)得像逝薪,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蝴罪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容