OkDownload主要功能
- 結合OkGo的request進行網絡請求,支持與OkGo保持相同的配置方法和傳參方式
- 支持斷點下載,支持突然斷網,強殺進程后,繼續斷點下載
- 每個下載任務具有無狀態、下載、暫停、等待、出錯、完成共六種狀態
- 所有下載任務按照tag區分,切記不同的任務必須使用不一樣的tag,否者斷點會發生錯亂
- 相同的下載url地址,如果使用不一樣的tag,也會認為是兩個下載任務
- 不同的下載url地址,如果使用相同的tag,會認為是同一個任務,導致斷點錯亂
- 默認同時下載數量為3個,默認下載路徑
/storage/emulated/0/download
,下載路徑和下載數量都可以在代碼中配置 - 下載文件名可以自己定義,也可以不傳,讓框架自動獲取文件名
注意!!!
OkGo與OkDownload的區別就是,OkGo只是簡單的做一個下載功能,不具備斷點下載,暫停等操作,但是這在很多時候已經能滿足需要了。
而有些app需要有一個下載列表的功能,就像迅雷下載一樣,每個下載任務可以暫停,可以繼續,可以重新下載,可以有下載優先級,這時候OkDownload就有用了。
OkDownload只是對OkGo功能的一個擴展升級,如果你不需要使用下載管理的功能,那么就不需要導入該包。
廢話不多說,看看怎么用。
全局配置
直接上代碼,如圖:
- 支持設置全局下載文件夾,以后每個任務如果不設置文件夾,將默認用這個路徑,如果不設置,默認就是圖中的目錄。
- 可以設置同時下載的任務數量,如果不設置,默認就是3個,改方法只在第一次調用時生效,以后無效
- 可以設置所有任務的監聽,比如全部任務開始的時候做點事情,全部完成后做點事情。
- 記得監聽可以取消,否則可能會發生內存泄露。
此外,OkDownload不僅有全局相關的設置,還有對全部任務的同時操作能力。 例如如下方法:
- startAll():開始所有任務,或者繼續下載所有暫停的任務都是這個方法
- pauseAll():將全部下載中的任務暫停
- removeAll():移除所有任務,無論這個任務是在下載中、暫停、完成還是其他任何狀態,都可以直接移除這個任務,他有一個重載方法,接受一個boolen參數,true表示刪除任務的時候同時刪除文件,false表示只刪除任務,但是文件保留在手機上。不傳的話,默認為false,即不刪除文件。
- removeTask():根據tag移除任務
- getTaskMap():獲取當前所有下載任務的map
- getTask():根據tag獲取任務
- hasTask():標識為tag的任務是否存在
Progress對象
在使用之前,先介紹一個對象,這個對象是我們在做進度回調時,無論是上傳進度回調,還是下載進度回調,無論是使用OkGo
,OkDownload
,或則OkUpload
時候,都會碰到一個對象,叫Progress
對象,他保存了當前進度的很多信息,源碼中的定義如下,字段很多,但是我覺得看字段名和后面的注釋就能知道是什么作用了,所以如果你需要什么進度信息,直接就能取出來。
唯一需要說明的一點是,最頂部對應的6個靜態常量,表示下載的狀態,其實就是對應下面的status
字段的,該字段永遠是這六個狀態中的一個。
基本斷點下載方法
我們首先看看一個最基本的下載任務是怎么寫的,先給代碼如下: 以上的幾行代碼就完成了下載文件的斷點下載,可以開始,暫停,繼續,重新下載等,是不是很簡單,關於以上代碼,詳細說明如下:
- 構建一個下載請求Request,這個構建方法和OkGo是一樣的,params參數和headers參數是只是演示使用,一切OkGo的使用方法,這里都是一樣的。
- 構建下載任務,使用OkDownload中的request方法,傳入一個tag和我們上一步創建的request對象,創建出下載任務,其他的方法我們下文在細講。
- 啟動任務,我們已經得到了DownloadTask任務對象,那么簡單調用start啟動他就好了,同時他還支持這么幾個方法:
- start():開始一個新任務,或者繼續下載暫停的任務都是這個方法
- pause():將一個下載中的任務暫停
- remove():移除一個任務,無論這個任務是在下載中、暫停、完成還是其他任何狀態,都可以直接移除這個任務,他有一個重載方法,接受一個boolen參數,true表示刪除任務的時候同時刪除文件,false表示只刪除任務,但是文件保留在手機上。不傳的話,默認為false,即不刪除文件。
- restart():重新下載一個任務。重新下載會先刪除以前的任務,同時也會刪除文件,然后從頭開始重新下載該文件。
下載任務需要一個回調,我們上面隨意注冊一個打日志的回調,代碼如下: 控制台的輸出如下:
這個log要做以下幾點說明:
- onStart()方法是在下載請求之前執行的,所以可以做一些請求之前相關的事,比如修改請求參數,加密,顯示對話框等等。
- 服務端返回的響應碼是206,注意是206,這個很重要,只有206才能實現斷點下載,表示本次返回的是部分響應體,並不是全部的數據。
- 服務端一定要返回Content-Length,注意,是一定要返回Content-Length這個響應頭,如果沒有,該值默認是-1,這個值表示當前要下載的文件有多大,如果服務端不給的話,客戶端在下載過程中是不可能知道我要下載的文件有多大的,所以常見的問題就是進度是負數。
- 下載文件的時候,響應體會打印一句話:
maybe [binary boby], emitted!
,這句話表示當前的數據是二進制文件,控制台沒法打印也沒必要打印出來,所以不用打印了。很多人經常拿着這個log告訴我出問題了,搞的我真是欲哭無淚,所以不要認為是bug,這是正常的。 - 下載完后,最后會調用onFinish(),不過我設計成在調用onFinish()之前,還會額外調用一次onProgress()方法,這樣的好處可以在onProgress方法中捕獲到所有的狀態變化,方便管理。
下載API介紹
以上的介紹只是簡單的說了一下使用方法,那么OkDownload究竟提供的api一共有哪些呢,我們來看看,保證滿足你的任何需求。
Request的構建詳細參考OkGo的用法,這里重點介紹DownloadTask的構建,這里面的方法一個個介紹:
- request():靜態方法創建DownloadTask對象,接受兩個參數,第一個參數是tag,表示當前任務的唯一標識,就像介紹中說的,所有下載任務按照tag區分,不同的任務必須使用不一樣的tag,否者斷點會發生錯亂,如果相同的下載url地址,如果使用不一樣的tag,也會認為是兩個下載任務,不同的下載url地址,如果使用相同的tag,也會認為是同一個任務,導致斷點錯亂。切記,切記!!
- priority():表示當前任務的下載優先級,他是一個int類型的值,只要在int的大小范圍內,數值越大,優先級越高,也就會優先下載。當然也可以不設置,默認優先級為0,當所有任務優先級都一樣的時候,就會按添加順序下載。
- floder():單獨指定當前下載任務的文件夾目錄,如果你是6.0以上的系統,記得下載的時候先自己獲取sd卡的運行時權限,否則文件夾創建不成功,無法下載。當然也可以不指定,默認下載路徑
/storage/emulated/0/download
。 - fileName():手動指定下載的文件名,一般來說是不需要手動指定的,也建議不要自己指定,除非你明確知道你要下載的是什么,或者你想改成你自己的文件名。如果不指定,文件名將按照以下規則自動獲取,獲取規則與OkGo文件下載獲取規則一致,點擊這里查看。
- extra():這個方法相當於數據庫的擴展字段,我們知道我們斷點下載是需要保存下載進度信息的,而我們這個框架是保存在數據庫中,數據庫的字段都是寫死的,如果用戶想在我們的下載數據庫中保存自己的數據就做不到了,所以我們這里提供了三個擴展字段,允許用戶保存自己想要的數據,如果不需要的話,也不用調用該方法。
- register():這是個注冊監聽的方法,我們既然要下載文件,那么我們肯定要知道下載的進度和狀態是吧,就在這里注冊我們需要的監聽,監聽可以注冊多個,同時生效,當狀態發生改變的時候,每個監聽都會收到通知。當然如果你只是想下載一個文件,不關心他的回調,那么你不用注冊任何回調。
細心你一定會發現我有個方法沒有講,那就是save()
方法,這個方法很重要,重要到我要單獨拿出來說他。我們先看看這個方法的源碼: 簡直簡單到不行,就一行代碼,就是把當前的progress進度對象,保存到數據庫。
想想,如果數據庫中沒有你這條記錄,然后你還開始下載,我如何去更新這個記錄,一旦你這么做,OkDownload就會拋出一個異常如下,告訴你必須在start()前,先調用save()方法。 所以我們知道了,save()用在什么地方呢?
- 如果你是第一次下載這個標識為這個tag的任務,那么你一定要調用save()方法,先將該數據寫入數據庫。
- 如果你對標識為tag的任務進行了一些參數的修改,比如修改了extra數據,修改了下載目錄,下載文件夾等等,也必須調用save()方法,更新數據庫。
到這里就基本把請求的相關api介紹完了。
ProgressListener對象
我們上面介紹了一些請求的API,這里我們看看回調的API,下載回調用的DownloadListener,源碼如下: 實現了一個接口,定義如下:
需要說明的是以下幾點:
- DownloadListener的構造方法需要傳一個tag,這個tag唯一標識當前listener,主要目的是方便取消監聽,同時可以防止數據錯亂。
- 你看到的這5個回調方法,全部在主線程回調,不需要你自己開任何線程。
- onProgress方法不僅在任何進度變化的時候會被回調,任何下載狀態變化的時候也會回調,所以很多時候,想監聽狀態變化,也在這個方法中就夠了。
監聽取消
我們都知道,網絡請求是個長時間的事情,如果你在下載過程中,關閉了Activity,而你的回調又還在這個頁面中更新UI,那么久會發生內存泄露,為了避免這種情況的發生,推薦以下做法:
- 如果你要實現頁面進度的實時刷新,那么該監聽就只需要在當前頁面有效就行了,關閉頁面之前,記得把監聽移除,這樣就不會泄露了,代碼如下,我們看到了取消監聽的時候需要傳遞一個tag,那么這個tag是你創建監聽的時候傳遞的那個tag,不是創建下載任務的那個task,不要搞混了。監聽是監聽的,任務是任務的。
- 有人問了,我要是取消了,我想在其他頁面,其他地方監聽到這個下載任務怎么辦?你當然可以繼續注冊一個監聽,這個監聽里面不要干與UI相關的事,不要持有Activity的引用,那么你這個監聽是不會造成泄露的。
- 如果你還不放心,你可以自己額外起一個service,在service中下載任務,注冊監聽,這樣就不怕泄露了。
DownloadManager
我們知道了下載任務是保存在數據庫中的,那么數據庫中的數據我們能不能直接查看呢,答案是可以的,就是這DownloadManager類,這個類是對下載任務的數據庫進行增刪改查的管理類,切記不要自己修改這里面的數據,不要直接使用DownloadManager的api,而應該使用OkDownload的api,這樣才能確保數據的完整性和准確性。
介紹3個常用的方法: 分別表示從數據庫中獲取所有的下載記錄,已完成的下載記錄和未完成的下載記錄。
這里可以看出來,我們獲取是Progress對象的列表,我們拿到這個列表一般是沒什么用的,我們也不能去修改這里面的數據。
所以,這個類的方法一般還需要和OkDownload的方法配合一下使用,一般用法就是獲取數據后,將數據庫的集合數據恢復成下載任務數據,代碼如下:
這里我們看到了這個OkDownload.restore()這個方法,他的作用就是把數據庫中的記錄,恢復成下載任務。我們拿到了這個任務列表,這個DownloadTask對象,操作方法不就是文檔上面講的那堆了不。
結束
到這里OkDownload的用法就和全部API基本就講完了。
什么?你說你用的時候列表進度錯亂?這個怪我嘍?
關於在列表中進度錯亂的問題,這個其實和庫一點關系沒有,完全是因為ListView或者RecyclerView復用問題導致的,你要是不會解決,好好看看Demo怎么解決的吧。