Android高速下載器實(shí)現(xiàn)思路——單個(gè)任務(wù)的提速與優(yōu)化

前言

最近過(guò)了金三銀四的金三阅畴,順利拿到了暑假實(shí)習(xí)生的offer。實(shí)習(xí)部門leader給我布置了入職前學(xué)習(xí)任務(wù)鳍悠,強(qiáng)化多線程哩都、數(shù)據(jù)庫(kù)方面的知識(shí)魁兼,并建議我實(shí)現(xiàn)一個(gè)和他們產(chǎn)品中類似的下載器。

實(shí)現(xiàn)思路

本文的重點(diǎn)在下載部分的實(shí)現(xiàn)漠嵌。目前我也正在做單個(gè)任務(wù)下載開發(fā)與優(yōu)化咐汞。后續(xù)更新完成后如果有好的思路也會(huì)分享給大家。
項(xiàng)目地址是:https://github.com/SirLYC/Yuchuan-Downloader
(處于開發(fā)中)

斷點(diǎn)下載

首先献雅,下載器有斷點(diǎn)續(xù)傳功能碉考,斷點(diǎn)續(xù)傳實(shí)現(xiàn)的基礎(chǔ)知識(shí)就是HTTP協(xié)議中的Range頭部。比如挺身,一個(gè)文件有500bytes,我要從第200個(gè)bytes下載锌仅,就在請(qǐng)求的頭部添加一個(gè)key為Range的項(xiàng)章钾,內(nèi)容是bytes=200-。因此热芹,在實(shí)現(xiàn)的時(shí)候贱傀,我們需要記錄當(dāng)前的下載量,在恢復(fù)下載的時(shí)候伊脓,就可以從上次的當(dāng)前下載量開始下載府寒,節(jié)省用戶流量。

但是并不是所有的服務(wù)器都支持?jǐn)帱c(diǎn)下載报腔。因此株搔,可以在正式的下載前先發(fā)一個(gè)請(qǐng)求,在請(qǐng)求中添加Range字段纯蛾,順帶也可以通過(guò)這種方式獲取文件長(zhǎng)度(ContentLength首部)纤房。

這里簡(jiǎn)單說(shuō)一下下載文件的原理。在一個(gè)GET請(qǐng)求時(shí)翻诉,服務(wù)器首先會(huì)把頭部報(bào)文全部返回給你炮姨,如果是下載文件捌刮,一般來(lái)說(shuō)都是流下載,有一個(gè)標(biāo)志會(huì)告訴你responseBody是流舒岸。而HTTP又是基于TCP的绅作,這個(gè)流實(shí)際上就是TCP的流,在Java中對(duì)應(yīng)的就是InputStream蛾派。流可以看作是一個(gè)只能向后走的指針俄认,指針指向下一個(gè)待讀取的字節(jié),并且讀取了一個(gè)才能讀下一個(gè)碍脏。因此梭依,如果暫停恢復(fù)不用部分請(qǐng)求的話典尾,你必須得把前面下載過(guò)的字節(jié)全部接受一遍役拴,這顯然浪費(fèi)了時(shí)間和流量。

多線程下載

首先要知道钾埂,多線程是基于斷點(diǎn)下載的原理河闰。一個(gè)文件實(shí)際上就是二進(jìn)制數(shù)據(jù),把文件拆分成多個(gè)段褥紫,每個(gè)線程下載各自的段姜性。因此每個(gè)線程在請(qǐng)求時(shí)需要控制文件起始和結(jié)尾,給每一個(gè)線程分配下載的段髓考。因此部念,不支持?jǐn)帱c(diǎn)續(xù)傳的服務(wù)器是不能用多線程下載的。

那為什么多線程下載可以提速呢氨菇?首先比較顯然的一點(diǎn)是多線程可以利用CPU多核的特性儡炼,在相同時(shí)間內(nèi)完成更多的任務(wù)。但事實(shí)上基于這一點(diǎn)不會(huì)提高多大的速度查蓉,因?yàn)榻邮斩说目値捠且欢ǖ奈谘O胂笠粋€(gè)這個(gè)場(chǎng)景:


image

上面的小水管就是我們的服務(wù)端連接,每個(gè)連接限制了最大帶寬豌研。大水管就是接收端妹田,接收端帶寬一定。當(dāng)我們啟用一個(gè)小水管時(shí)鹃共,我們可以獲得的最大流速是min(小水管鬼佣、大水管)。當(dāng)我們啟用多個(gè)水管時(shí)及汉,最大速度是min(小水管1+小水管2+...+小水管n沮趣,大水管)】浪妫可見房铭,在這種場(chǎng)景下的多線程驻龟,瓶頸就不會(huì)再是服務(wù)端的帶寬限制。

那線程是不是越多越好呢缸匪? 顯然這是不對(duì)的翁狐。線程本身就是一個(gè)很重的對(duì)象,創(chuàng)建線程凌蔬、多線程調(diào)度管理會(huì)占用CPU時(shí)間露懒,會(huì)減少用戶時(shí)間比例。另外就是多線程對(duì)內(nèi)存的占用也是一個(gè)問(wèn)題砂心。因此懈词,啟動(dòng)的下載線程數(shù)要有限制。

下載與寫線程分開

以前寫下載器時(shí)辩诞,常見的下載模式是

// 偽代碼
while (data remains to read) {
    buffer = inputstream.read(bufferSize)
    outputstream.write(buffer)
}

在多線程的情況下大概是這樣的


image

當(dāng)時(shí)現(xiàn)場(chǎng)面試的時(shí)候我也講下載器可以這么實(shí)現(xiàn)坎弯,結(jié)果面試官上來(lái)問(wèn)一句,讀和寫真的要放在一個(gè)線程译暂?
從目前來(lái)講抠忘,寫磁盤的速度一般都是遠(yuǎn)大于網(wǎng)絡(luò)獲取的速度的。如果我們能把寫數(shù)據(jù)放在一個(gè)單獨(dú)的線程里外永,假設(shè)3個(gè)線程以相同的速度讀取相同大小的網(wǎng)絡(luò)字節(jié)流放在緩沖區(qū)崎脉,每個(gè)線程都把各自的緩沖區(qū)送入寫線程,然后又各自去讀網(wǎng)絡(luò)數(shù)據(jù)伯顶。因?yàn)槲覀儗懙乃俣却笥诰W(wǎng)絡(luò)下載速度的囚灼,因此在下一次3個(gè)緩沖區(qū)送入前是可以寫完的,這樣在理想情況下就節(jié)省了1次寫磁盤的時(shí)間祭衩。

但在實(shí)際實(shí)現(xiàn)時(shí)啦撮,有很多需要注意的地方。首先下載線程不能無(wú)限制的下載汪厨。如果寫線程阻塞了,下載線程還在不停下載的話愉择,緩沖區(qū)會(huì)越來(lái)越大劫乱,造成OOM。另外就是緩沖區(qū)的交換锥涕,寫線程需要拿衷戈,下載線程需要送,這是一個(gè)典型的消費(fèi)者——生產(chǎn)者模式层坠。這方面的實(shí)現(xiàn)文章就多了殖妇,最終我是選用的BlockQueue來(lái)實(shí)現(xiàn)。大致的流程如下:

image

上述流程中破花,還有很多未包括所有內(nèi)容谦趣,比如錯(cuò)誤處理疲吸,狀態(tài)轉(zhuǎn)換等。實(shí)際上前鹅,要寫一個(gè)用戶體驗(yàn)好摘悴,性能好的下載器是一件很不容易的事。

后續(xù)

目前舰绘,我的項(xiàng)目上實(shí)現(xiàn)的只有單任務(wù)多線程的下載蹂喻,多任務(wù)、下載信息本地保存等還未實(shí)現(xiàn)捂寿。

除了這些以外口四,我還會(huì)考慮加入多進(jìn)程的架構(gòu),可以實(shí)現(xiàn)ui退出后的離線下載秦陋。歡迎大家clone跑sample或者提一些意見蔓彩!

再次掛上項(xiàng)目地址:https://github.com/SirLYC/Yuchuan-Downloader

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市踱侣,隨后出現(xiàn)的幾起案子粪小,更是在濱河造成了極大的恐慌,老刑警劉巖抡句,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件探膊,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡待榔,警方通過(guò)查閱死者的電腦和手機(jī)逞壁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)锐锣,“玉大人腌闯,你說(shuō)我怎么就攤上這事〉胥荆” “怎么了姿骏?”我有些...
    開封第一講書人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)斤彼。 經(jīng)常有香客問(wèn)我分瘦,道長(zhǎng),這世上最難降的妖魔是什么琉苇? 我笑而不...
    開封第一講書人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任嘲玫,我火速辦了婚禮,結(jié)果婚禮上并扇,老公的妹妹穿的比我還像新娘去团。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開白布土陪。 她就那樣靜靜地躺著昼汗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪旺坠。 梳的紋絲不亂的頭發(fā)上乔遮,一...
    開封第一講書人閱讀 51,488評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音取刃,去河邊找鬼蹋肮。 笑死,一個(gè)胖子當(dāng)著我的面吹牛璧疗,可吹牛的內(nèi)容都是我干的坯辩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼崩侠,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼漆魔!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起却音,我...
    開封第一講書人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤改抡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后系瓢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谦炒,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡矿卑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年悟耘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了汤纸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡骗绕,死狀恐怖藐窄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情酬土,我是刑警寧澤荆忍,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站撤缴,受9級(jí)特大地震影響东揣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜腹泌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望尔觉。 院中可真熱鬧凉袱,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至涤躲,卻和暖如春棺耍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背种樱。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工蒙袍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嫩挤。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓害幅,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親岂昭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子以现,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

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