HTTP那些事

網(wǎng)絡(luò)請(qǐng)求API

在Android上昆咽,原生API有兩個(gè)实辑,HttpUrlConnection和HttpClient,它們對(duì)封裝Socket進(jìn)行封裝季春,讓HTTP請(qǐng)求變得簡(jiǎn)單。這應(yīng)該也算框架吧消返?

想象下载弄,如果沒(méi)有HttpUrlConnection和HttpClient,一次性的API請(qǐng)求得有多麻煩撵颊。

現(xiàn)在宇攻,我們又多了一種OkHttp,Square出品倡勇。當(dāng)然底層還是封裝socket逞刷。為什么,為什么還要再出一個(gè)OkHttp妻熊,吃飽了撐的夸浅?肯定不是,那究竟有什么好的扔役?自己動(dòng)手查一下吧帆喇。

我們假設(shè)一下,應(yīng)該是HttpUrlConnection和HttpClient自身有bug和缺陷亿胸,所以才會(huì)再根據(jù)如今的網(wǎng)絡(luò)情況設(shè)計(jì)OkHttp吧坯钦。

如果你看過(guò)Volley的源代碼预皇,就知道當(dāng)SDK>9時(shí),默認(rèn)使用HttpUrlConnection婉刀,<9的就用HttpClient吟温。

http01.png

既然>9采用HttpUrlConnection了,那說(shuō)明突颊,再以后的版本中由Android修復(fù)了鲁豪,那HttpClient呢,Apache更新維護(hù)太慢律秃,基本要被淘汰爬橡。

如果說(shuō)你的項(xiàng)目還在用HttpClient,甚至還在為HttpClient的某些bug而苦惱友绝,那么你該考慮是否該換了。畢竟現(xiàn)實(shí)不可能給你那么多時(shí)間去調(diào)研debug肝劲。

當(dāng)然OkHttp也是有bug的迁客,從github上的issues就能知道,如果你用OkHttp發(fā)現(xiàn)了bug辞槐,又不知道如何解決掷漱,不妨去那看看。

說(shuō)了這么多榄檬,Stay想表達(dá)的有兩層意思:

  1. 不妨使用新技術(shù)來(lái)解決老技術(shù)的缺陷卜范,就好像如果現(xiàn)在還有人用TabActivity,TabHost鹿榜,那給人感覺(jué)一定是做外包出身的海雪。
  2. 嘗試新技術(shù)的成本不高的,如果它開源舱殿,并且有release版本(1.0+)奥裸,你都可以集成試試。新技術(shù)都是為了更好的開發(fā)而被設(shè)計(jì)出來(lái)的沪袭,就算它不是最優(yōu)的解決方案湾宙,至少設(shè)計(jì)理念,解決思路是值得參考的冈绊。

今天下午花了點(diǎn)時(shí)間侠鳄,粗略的過(guò)了一遍OkHttp,有意思的是死宣,為了讓大家無(wú)縫集成伟恶,也是蠻拼的,額外提供HttpUrlConnection和HttpClient的寫法毅该。你只需要再依賴okhttp-urlconnection.jar或者okhttp-apache.jar就可以了知押。

本來(lái)Stay是打算用OkHttp自己的請(qǐng)求API集成的自己的網(wǎng)絡(luò)框架里叹螟,搗鼓了半天,怪麻煩的台盯,API來(lái)來(lái)回回要找半天罢绽,索性就直接換成HttpUrlConnection的形式寫了。

http02.png

see, 集成起來(lái)太方便了静盅。簡(jiǎn)單的測(cè)試了下良价,get,post蒿叠,上傳明垢,下載都沒(méi)問(wèn)題。其他就沒(méi)再深入了市咽。

OkHttp的示例都很簡(jiǎn)單痊银,有很多配置(ssl, cookies, headers, timeout)沒(méi)詳細(xì)說(shuō)明,那如果你想配置施绎,該怎么做捏溯革。可以看源碼谷醉,也可以看一些網(wǎng)絡(luò)請(qǐng)求框架如:Retrofit(Square的網(wǎng)絡(luò)請(qǐng)求框架致稀,默認(rèn)集成OkHttp)源碼中的API配置。

http03.png

對(duì)于這類底層API創(chuàng)新俱尼,還是比較少的抖单,幾年內(nèi)能出一個(gè)就不錯(cuò)了,畢竟對(duì)HTTP協(xié)議融會(huì)貫通而且能優(yōu)化的大牛少之又少遇八。對(duì)于一般的苦逼開發(fā)者來(lái)說(shuō)矛绘,能做到及時(shí)跟進(jìn)已屬不易。

多嘗試新技術(shù)刃永,至少可以晚些結(jié)束自己的程序員生涯 :)

JSON數(shù)據(jù)

隨著Android的發(fā)展蔑歌,各路大神的貢獻(xiàn),我們可用的輪子越來(lái)越多揽碘。比如HTTP請(qǐng)求框架次屠,有自家的Volley,Square的okhttp, async-http-lib, 還有聚合版的xUtils以及AFinal雳刺。我想你肯定用過(guò)其中一個(gè)劫灶。

當(dāng)然Stay今天不是來(lái)科普的,而是來(lái)跟大家一起思考一個(gè)問(wèn)題的掖桦。我們暫且不提他們?cè)趦?nèi)部做了多少優(yōu)化本昏,我們就說(shuō)lib的返回?cái)?shù)據(jù)。

在常用的http請(qǐng)求的返回值中枪汪,文件涌穆,JSON占絕大多數(shù)(圖片有其他框架怔昨,這里不考慮)。文件下載都有專門的response宿稀,會(huì)幫你下載到制定路徑趁舀,這個(gè)肯定都支持。那JSON呢祝沸?貌似都返回一個(gè)JSONObject或者JSONArray矮烹。

我去,做好事得做全啊罩锐,返回JSONObject是個(gè)什么鬼奉狈,難道還得自己動(dòng)手寫解析反序列化成自己要得對(duì)象?那是最低級(jí)的程序員干的事涩惑。好在我們都不傻仁期,還有GSON,fastJson竭恬,Jackson幫我們來(lái)完成這步轉(zhuǎn)化跛蛋。

比方說(shuō)服務(wù)器返回的數(shù)據(jù):(雙引號(hào)沒(méi)加,占位置萍聊,別噴)

{name:stay, age:17, job:soho}

對(duì)應(yīng)的對(duì)象:

Class User{
  public String name,
  public int age,
  public String job
}

好问芬,那我們只需要在response回調(diào)時(shí)拿到result悦析,調(diào)用json-lib反序列化就可以了寿桨,比如這樣:

User user = gson.fromJson(result, User.class)

現(xiàn)在我們就可以使用user對(duì)象來(lái)更新UI了對(duì)吧。就多了一行代碼强戴,沒(méi)強(qiáng)迫癥的也就忍過(guò)去了亭螟。

接下來(lái)我們?cè)倏聪旅嬉环Njson數(shù)據(jù):

{resCode:200, data:{name:Stay, age:17, job:soho}, msg:success}
{resCode:401, data:{}, msg:token invalid}

我去,這是什么鬼骑歹,不好好遵守http協(xié)議预烙,統(tǒng)一返回200是什么鬼,token不合法給我返回401 error code不好嗎道媚。扁掸。別說(shuō),很多公司都這么定義返回?cái)?shù)據(jù)的

這樣我們?cè)趺崔k最域。谴分。多寫一步解析咯。

JSONObject json = new JSONObject(result)
JSONObject data = json.optJSONObject("data")
if(data != null){
  User user = gson.fromJson(data.toString(), User.class)
}

天啊镀脂,即使沒(méi)強(qiáng)迫癥牺蹄,大概也會(huì)受不了每個(gè)API請(qǐng)求都寫這么多代碼了吧。

BB了這么多薄翅,大家應(yīng)該懂我想表達(dá)什么了吧沙兰?

為什么不直接將json轉(zhuǎn)換成我們要的對(duì)象User再回調(diào)呢氓奈?

而且在json數(shù)據(jù)大的情況下,反序列化還是耗時(shí)操作鼎天,有可能會(huì)卡UI的好嗎舀奶。

這可能么?當(dāng)然可以训措,不然Stay鋪墊這么多干嘛伪节。不過(guò)在Stay說(shuō)解決方案之前,大家可以試著自己考慮下實(shí)現(xiàn)绩鸣。

  1. 我們拿到的是String怀大,格式是JSON
  2. 每次拿到JSON String,我們都來(lái)做了一步反序列化對(duì)象操作
  3. gson.fromJson需要兩個(gè)參數(shù)(String JSON呀闻,Class dest)
  4. 回調(diào)參數(shù)得變成onResponse(User user)
  5. 框架層得知道Class dest

如果能把這些事情想清楚化借,你就可以很順利得擴(kuò)展那些開源框架了,以后你也再不用手寫json解析了捡多。

就說(shuō)這么多蓖康,留點(diǎn)時(shí)間給大家自己思考下~

最后說(shuō)下需要用到得知識(shí)點(diǎn):泛型,反射

JSON反序列化

上文中垒手,我們提到蒜焊,能否讓我們的HTTP框架幫我們完成自動(dòng)反序列化的操作。同時(shí)也給大家做了些提示:泛型和反射科贬。

現(xiàn)在我們以Volley為例:

在Volley中有三種Request:FileRequest泳梆,StringRequest,ImageRequest榜掌。

JSON數(shù)據(jù)也是字符串优妙,所以我們要重寫StringRequest中的部分方法就可以咯。

看下StringRequest源碼憎账,你會(huì)看到解析服務(wù)器byte[]到String的是parseNetworkResponse(NetworkResponse response)套硼,解析完String直接就return給外層了。

這里我們也采用相同的方式胞皱,創(chuàng)建一個(gè)GsonRequest< T >繼承Request< T >, 至于實(shí)現(xiàn)邪意,先把StringRequest的代碼copy過(guò)來(lái)。唯一不同的是反砌,StringRequest因?yàn)橹付ǚ祷豐tring類型數(shù)據(jù)所以不需要泛型雾鬼。

在parseNetworkResponse(NetworkResponse response)中,我們引入gson來(lái)反序列化json string于颖,T的class怎么辦呢呆贿?你可以通過(guò)外層顯式的傳進(jìn)來(lái)或者通過(guò)反射來(lái)拿類上的泛型T的type。兩種都可以。

具體到代碼:

json01.png

擴(kuò)展完畢做入,你只需要new GsonRequest冒晰,聲明好泛型T,等待接收t對(duì)象回調(diào)就好啦竟块。

json02.png

像這樣的擴(kuò)展還有很多壶运,框架不是萬(wàn)能的,要合理的根據(jù)自己的需求定制你想要的框架浪秘。

最后蒋情,留個(gè)問(wèn)題給大家,如果是服務(wù)器返回了1M的JSON數(shù)據(jù)耸携,還能用上述擴(kuò)展么棵癣?如果不可以,那該怎么辦呢夺衍?

超大JSON文本

在JSON反序列化一文的最后狈谊,有提到,如果有1M的JSON文本應(yīng)該如何來(lái)解析沟沙?

1M的JSON String河劝,不管用GSON,fastjson矛紫,jackson赎瞎,估計(jì)都要OOM了吧。本來(lái)我想說(shuō)200M的JSON數(shù)據(jù)的颊咬,想想這太坑了务甥,就改說(shuō)1M了。

答案贪染,用JsonReader讀流缓呛。比如說(shuō):

public User readUser(JsonReader reader) throws IOException {
    String username = null;
    int followersCount = -1;

    reader.beginObject();
    while (reader.hasNext()) {
        String name = reader.nextName();
        if (name.equals("name")) {
            username = reader.nextString();
        } else if (name.equals("followers_count")) {
            followersCount = reader.nextInt();
        } else {
            reader.skipValue();
        }
    }
    reader.endObject();
    return new User(username, followersCount);
}

我去催享,要手寫JSON解析了杭隙,這太麻煩了吧。因妙。痰憎。
但是你想,跟性能比起來(lái)攀涵,這些體力也不算什么了吧铣耘。

上述沒(méi)太多特別的地方,你可以直接看JsonReader的源碼注釋以故,里面有詳細(xì)的用法示例蜗细。

在這里呢,我們先說(shuō)說(shuō)如何讓JsonReader來(lái)讀大JSON文本。

FileReader in = new FileReader(path);
JsonReader reader = new JsonReader(in);

首先炉媒,你得先把JSON文本以文件的形式存到SD卡上踪区。再通過(guò)FileReader拿到文件流,再通過(guò)JsonReader來(lái)讀流吊骤,讀流的方式也就意味著是順序讀的缎岗,所以即使它不是正確的json格式,也會(huì)一直讀到錯(cuò)誤為止白粉。

JsonReader對(duì)手寫的json解析語(yǔ)法非常嚴(yán)格传泊,寫錯(cuò)是非常頭疼的事,另外建議把nodeName變?yōu)槌A咳プ雠袛嘌及停蝗灰院蟾淖兞棵每尴埂?/p>

當(dāng)然眷细,Stay肯定不會(huì)講這么簡(jiǎn)單的東西,我們?cè)趺锤鶫TTP框架結(jié)合在一起呢鹃祖?這解析過(guò)程肯定也是耗時(shí)操作薪鹦,我總不能先用框架把數(shù)據(jù)當(dāng)文件下載下來(lái),然后再開一個(gè)線程來(lái)解析吧惯豆。這才是最蛋疼的地方池磁。

可惜原生Volley都不支持文件下載,這里我就拿自己的HTTP框架做演示了楷兽。

簡(jiǎn)單說(shuō)下實(shí)現(xiàn)過(guò)程:

  1. 首先寫個(gè)接口地熄,比如JsonReaderable,里面定義一個(gè)方法readFromJson(JsonReader reader)

  2. 讓你想要被反序列化的對(duì)象pojo實(shí)現(xiàn)這個(gè)接口芯杀,比如這樣這里寫
    json01.png
  3. 讓框架先把數(shù)據(jù)當(dāng)文件下載到SD卡

  4. 在callback之前再bindData端考,比如這樣這里寫
    json02.png

    這樣就能將json數(shù)據(jù)自動(dòng)反序列化成對(duì)象callback回去了。你只需要在每個(gè)對(duì)象pojo中實(shí)現(xiàn)readFromJson方法就好了揭厚。

  5. 如果是jsonarray怎么辦却特,我們要返回一個(gè)ArrayList啊。比如這樣這里寫
    json03.png
  6. 一個(gè)好的框架相當(dāng)?shù)闹匾∩冈玻覀冊(cè)賮?lái)看外層的調(diào)用這里寫
    json04.png

應(yīng)該不用解釋吧裂明,都能看懂。
這種情況雖然比較少見(jiàn)太援,但在一些erp啊闽晦,sap項(xiàng)目中經(jīng)常會(huì)遇到(別問(wèn)Stay怎么知道)如果你也見(jiàn)過(guò)Android上500M的數(shù)據(jù)庫(kù),那這些心得你都能自己領(lǐng)悟到了提岔。

現(xiàn)在我們?cè)贏pp中基本采取的都是分頁(yè)仙蛉,一般來(lái)說(shuō)不需要用JsonReader,但如果Json數(shù)據(jù)超過(guò)10K以上碱蒙,pojo的復(fù)雜度特別高荠瘪,并且還有嵌套時(shí),你應(yīng)該考慮使用。

你也許會(huì)問(wèn)哀墓,500M鞭莽,即使用JsonReader讀流生成對(duì)象了,內(nèi)存也裝不下呀麸祷。沒(méi)事澎怒,你可以通過(guò)ormapping型數(shù)據(jù)庫(kù)框架來(lái)存數(shù)據(jù),比如說(shuō)讀200個(gè)對(duì)象存一次阶牍,清一次喷面。或者你可以用接口回調(diào)的方式扔給外層處理走孽,

onPartialDataBinding(ArrayList list)

其實(shí)這個(gè)擴(kuò)展其他第三方框架也沒(méi)什么問(wèn)題惧辈,只要思路有了,實(shí)現(xiàn)起來(lái)也就很容易了磕瓷。

框架最好是根據(jù)App具體的需求以及使用場(chǎng)景來(lái)定制盒齿,僅會(huì)調(diào)用哪些開源lib,看不懂困食,改不了边翁,這樣只能讓自己在技術(shù)路上越走越窄。

就寫到這里硕盹,別問(wèn)Stay要代碼哈符匾,只講思維與解決方案。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瘩例,一起剝皮案震驚了整個(gè)濱河市啊胶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌垛贤,老刑警劉巖焰坪,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異聘惦,居然都是意外死亡某饰,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門部凑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)露乏,“玉大人碧浊,你說(shuō)我怎么就攤上這事涂邀。” “怎么了箱锐?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵比勉,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)浩聋,這世上最難降的妖魔是什么观蜗? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮衣洁,結(jié)果婚禮上墓捻,老公的妹妹穿的比我還像新娘。我一直安慰自己坊夫,他們只是感情好砖第,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著环凿,像睡著了一般梧兼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上智听,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天羽杰,我揣著相機(jī)與錄音,去河邊找鬼到推。 笑死考赛,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的莉测。 我是一名探鬼主播欲虚,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼悔雹!你這毒婦竟也來(lái)了复哆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤腌零,失蹤者是張志新(化名)和其女友劉穎梯找,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體益涧,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡锈锤,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了闲询。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片久免。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖扭弧,靈堂內(nèi)的尸體忽然破棺而出阎姥,到底是詐尸還是另有隱情,我是刑警寧澤鸽捻,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布呼巴,位于F島的核電站泽腮,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏衣赶。R本人自食惡果不足惜诊赊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望府瞄。 院中可真熱鬧碧磅,春花似錦、人聲如沸遵馆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)团搞。三九已至严望,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逻恐,已是汗流浹背像吻。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留复隆,地道東北人拨匆。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像挽拂,于是被迫代替她去往敵國(guó)和親惭每。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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