前言
先感謝大家對第一篇文章《Retrofit2的再封裝實戰(zhàn)—同步與異步請求》的支持城看,提筆之前反復(fù)考慮了很多女气,要怎么寫好多線程下載和斷點續(xù)傳?倒不是因為邏輯有多復(fù)雜测柠,是因為這里覆蓋的知識面太多了炼鞠,大量的多線程共享數(shù)據(jù),本地數(shù)據(jù)持久化轰胁,以及面向不同狀態(tài)如何反饋的問題谒主,這些東西每一塊其實都能掰碎了寫一寫,所以拼在一起赃阀,讓我不知道到底應(yīng)該從哪里入手開始寫霎肯。
既然上篇文章結(jié)尾說過了如果有人需要就分享這方面的封裝,為了不讓期待的朋友們失望榛斯。最后決定還是要硬著頭皮開始寫观游,至于從哪里開始寫,既然沒有好的想法驮俗,那就從頭開始懂缕,就當是重新寫一遍吧,如果在這過程中你有不通或者不認可的想法王凑,歡迎分享指正給我搪柑,大家一起進步吧。
(這篇文章是基于我的第一篇文章再拓展的索烹,所以建議您先移步上篇文章)
視頻演示:
點下載有等待時間拌屏,是因為我設(shè)置了每下載1m才回調(diào)。
正題
基礎(chǔ)概念
一术荤、什么是多線程下載?
Android主線程是不建議阻塞的每篷,通常的思路瓣戚,我們下載文件,都是在線程中執(zhí)行焦读,一個線程對應(yīng)一個下載通道子库,如果文件有上百M大小,況且你的網(wǎng)絡(luò)狀態(tài)不夠好矗晃,應(yīng)該怎么辦呢仑嗅?想一下如果我們把一個100M的文件,拆成4個線程,同時下載仓技,那該是多么幸福的事情啊鸵贬。無論是效率還是時間上,都會有大幅度的提升脖捻。那么在http上怎么實現(xiàn)分段下載呢阔逼?很簡單 在請求頭上加入Range屬性,像這樣設(shè)置bytes范圍就可以了地沮。
現(xiàn)在Response Body就會返回對應(yīng)范圍的字節(jié)流了嗜浮。好了,現(xiàn)在我們來拓展NetWorkRequest類摩疑,加入Download接口危融。
@Streaming
@GET
Call<ResponseBody> downloadFile(@Url String fileUrl,@Header("Range") String range);
}```
在NetWorkRequest中的中加入獲得DownLoadService接口代理類的方法,在init中初始化DownLoadService:
```mDownLoadService = mRetrofit.create(DownLoadService.class);```
向外提供mDownLoadService引用:
```public DownLoadService getDownLoadService() {
return mDownLoadService;
}```
好了雷袋,關(guān)于DownLoadService的初始化就結(jié)束了吉殃。
#####二、什么是斷點續(xù)傳片排?
簡單解釋就是 從哪摔倒的接著從哪站起來 這就意味著我們可以做取消寨腔、繼續(xù)下載的功能,或者從異常情況下恢復(fù)的再下載功能率寡。
關(guān)于如何保持下載數(shù)據(jù)問題迫卢,其實就是如何選擇數(shù)據(jù)持久化方式的問題。
Android數(shù)據(jù)持久化方式 Preferences SQL I/O ContentProvider冶共,這里選擇SQL應(yīng)該是沒有任何異議的乾蛤,至于你是選擇如何實現(xiàn)SQL功能,GreenDao還是Realm或者其他開源項目捅僵,這里不做推薦家卖,只要能夠?qū)崿F(xiàn)簡單的增刪改查功能就好,我選擇了原生的sqllite庙楚。只為了實現(xiàn)簡單功能上荡,并未做完整封裝,不考慮借鑒我的sqllite馒闷。
####程序結(jié)構(gòu)
介紹了基本概念的理解酪捡,相信大家對本次實戰(zhàn)的內(nèi)容應(yīng)該有大體了解了。下面上簡單的項目結(jié)構(gòu)圖并說一下思路:
![結(jié)構(gòu)圖.jpeg](http://upload-images.jianshu.io/upload_images/3376157-c16a695249dde192.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
Action:為行動的意思纳账,這里代表我們的Activity逛薇,F(xiàn)ragment,或者是Service疏虫。A永罚、B即代表著兩次請求行為。
DownLoadManager:不同的Action呢袱,通過統(tǒng)一入口DownLoadManager調(diào)用下載功能官扣,DownLoadManager是單列的。只提供唯一入口醇锚,就像很多開源框架Glide.with()、ImageLoader.load()一樣焊唬,這里是一個設(shè)計思路挟炬,外觀模式,也稱為門面模式谤祖,大家設(shè)計框架時候盡量這樣來做婿滓,只提供一個入口凸主,即為了以后好維護,也減少了耦合性额湘。我們里面代碼再怎么修改卿吐,也并不影響外面的調(diào)用。不要改一動全身那就尷尬了锋华。DownLoadManager提供創(chuàng)建createRequest方法嗡官,我們把每個Action的所有下載任務(wù)封裝為一個Request,DownLoadManager中只負責(zé)創(chuàng)建和取消下載任務(wù)毯焕,并維護下載任務(wù)的緩存衍腥。
Request:上面說道,Request即為一次下載任務(wù)纳猫。每個Request里面可能有幾十紧阔,甚至上百個url,每個url可能又會因為數(shù)據(jù)太大而分為多線程任務(wù)续担。每個Request里面維護著所有任務(wù)的緩存,基本的邏輯判斷活孩,數(shù)據(jù)持久化都在Request里面物遇,所以這是最有含金量的部分,怎么做到性能最優(yōu),怎么做到多線程數(shù)據(jù)的安全性询兴,都在這里面乃沙。
Task:每個url即為一個Task,通過Retrofit調(diào)用DownLoad接口诗舰,并進行數(shù)據(jù)存儲操作警儒,這里并沒有什么難度,只需要通過判斷不用的情況眶根,返回不通的狀態(tài)值蜀铲,對數(shù)據(jù)進行不同的操作。同時属百,根據(jù)你的業(yè)務(wù)邏輯记劝,來適配不通IOException行為。
簡單來講族扰,入口類DownLoadManager厌丑,調(diào)用者只需要關(guān)心這一個類,創(chuàng)建下載任務(wù)渔呵,DownLoadManager把每個任務(wù)封裝成一個Request對象怒竿,復(fù)雜的邏輯判斷全部交給Request來做,Task是真正執(zhí)行下載任務(wù)的對象扩氢,數(shù)據(jù)回調(diào)從下向上依次傳遞回Action耕驰。
####文章計劃
寫到這里,我想我介紹的思路應(yīng)該已經(jīng)很清晰了类茂,本次內(nèi)容分為三個部分介紹耍属。第一部分到這就結(jié)束了,只要是講一下概念和思路和DownloadService的初始化巩检,覺得有意思的朋友可以根據(jù)我上文說的思路厚骗,設(shè)計一下。下面兩篇文章是這樣計劃的兢哭,第二篇從DownloadTask和DownloadManager入口领舰,最后一篇放到重要的Request上,如果您感興趣迟螺,可以關(guān)注下這個系列冲秽。