=2_147_483_647 = 1.99GB
)的時候, 請使用FileDownloadLargeFileListener
而不是FileDownloadListener
(同理使用getLargeFileSofarBytes()
與getLargeFileTotalBytes()
)filedownloader.properties
將process.non-separate
置爲true
,能夠有效減小每次回調IPC帶來的I/O。在項目中引用:html
compile 'com.liulishuo.filedownloader:library:1.4.3'
若是是eclipse引入jar包參考: 這裏java
Application.onCreate
中public XXApplication extends Application{ ... @Override public void onCreate() { /** * 僅僅是緩存Application的Context,不耗時 */ FileDownloader.init(getApplicationContext); } ... }
FileDownloader.getImpl().create(url) .setPath(path) .setListener(new FileDownloadListener() { @Override protected void pending(BaseDownloadTask task, int soFarBytes, int totalBytes) { } @Override protected void connected(BaseDownloadTask task, String etag, boolean isContinue, int soFarBytes, int totalBytes) { } @Override protected void progress(BaseDownloadTask task, int soFarBytes, int totalBytes) { } @Override protected void blockComplete(BaseDownloadTask task) { } @Override protected void retry(final BaseDownloadTask task, final Throwable ex, final int retryingTimes, final int soFarBytes) { } @Override protected void completed(BaseDownloadTask task) { } @Override protected void paused(BaseDownloadTask task, int soFarBytes, int totalBytes) { } @Override protected void error(BaseDownloadTask task, Throwable e) { } @Override protected void warn(BaseDownloadTask task) { } }).start();
final FileDownloadListener queueTarget = new FileDownloadListener() { @Override protected void pending(BaseDownloadTask task, int soFarBytes, int totalBytes) { } @Override protected void connected(BaseDownloadTask task, String etag, boolean isContinue, int soFarBytes, int totalBytes) { } @Override protected void progress(BaseDownloadTask task, int soFarBytes, int totalBytes) { } @Override protected void blockComplete(BaseDownloadTask task) { } @Override protected void retry(final BaseDownloadTask task, final Throwable ex, final int retryingTimes, final int soFarBytes) { } @Override protected void completed(BaseDownloadTask task) { } @Override protected void paused(BaseDownloadTask task, int soFarBytes, int totalBytes) { } @Override protected void error(BaseDownloadTask task, Throwable e) { } @Override protected void warn(BaseDownloadTask task) { } }; // 第一種方式 : //for (String url : URLS) { // FileDownloader.getImpl().create(url) // .setCallbackProgressTimes(0) // 因爲是隊列任務, 這裏是咱們假設了如今不須要每一個任務都回調`FileDownloadListener#progress`, 咱們只關係每一個任務是否完成, 因此這裏這樣設置能夠頗有效的減小ipc. // .setListener(queueTarget) // .asInQueueTask() // .enqueue(); //} //if(serial){ // 串行執行該隊列 // FileDownloader.getImpl().start(queueTarget, true); // } // if(parallel){ // 並行執行該隊列 // FileDownloader.getImpl().start(queueTarget, false); //} // 第二種方式:
final FileDownloadQueueSet queueSet = new FileDownloadQueueSet(downloadListener); final List<BaseDownloadTask> tasks = new ArrayList<>(); for (int i = 0; i < count; i++) { tasks.add(FileDownloader.getImpl().create(Constant.URLS[i]).setTag(i + 1)); } queueSet.disableCallbackProgressTimes(); // 因爲是隊列任務, 這裏是咱們假設了如今不須要每一個任務都回調`FileDownloadListener#progress`, 咱們只關係每一個任務是否完成, 因此這裏這樣設置能夠頗有效的減小ipc. // 全部任務在下載失敗的時候都自動重試一次
queueSet.setAutoRetryTimes(1); if (serial) { // 串行執行該任務隊列
queueSet.downloadSequentially(tasks); // 若是你的任務不是一個List,能夠考慮使用下面的方式,可讀性更強 // queueSet.downloadSequentially( // FileDownloader.getImpl().create(url).setPath(...), // FileDownloader.getImpl().create(url).addHeader(...,...), // FileDownloader.getImpl().create(url).setPath(...) // );
} if (parallel) { // 並行執行該任務隊列
queueSet.downloadTogether(tasks); // 若是你的任務不是一個List,能夠考慮使用下面的方式,可讀性更強 // queueSet.downloadTogether( // FileDownloader.getImpl().create(url).setPath(...), // FileDownloader.getImpl().create(url).setPath(...), // FileDownloader.getImpl().create(url).setSyncCallback(true) // );
} // 串行任務動態管理也可使用FileDownloadSerialQueue。
FileDownloader
)全部的暫停,就是中止,會釋放全部資源而且停到全部相關線程,下次啓動的時候默認會斷點續傳android
方法名 | 備註 |
---|---|
init(Context) | 緩存Context,不會啓動下載進程 |
init(Context, InitCustomMaker) | 緩存Context,不會啓動下載進程;在下載進程啓動的時候,會傳入定製化組件 |
create(url:String) | 建立一個下載任務 |
start(listener:FileDownloadListener, isSerial:boolean) | 啓動是相同監聽器的任務,串行/並行啓動 |
pause(listener:FileDownloadListener) | 暫停啓動相同監聽器的任務 |
pauseAll(void) | 暫停全部任務 |
pause(downloadId) | 暫停downloadId的任務 |
clear(downloadId, targetFilePath) | 強制清理ID爲downloadId的任務在filedownloader中的數據 |
getSoFar(downloadId) | 得到下載Id爲downloadId的soFarBytes |
getTotal(downloadId) | 得到下載Id爲downloadId的totalBytes |
bindService(void) | 主動啓動下載進程(可事先調用該方法(能夠不調用),保證第一次下載的時候沒有啓動進程的速度消耗) |
unBindService(void) | 主動關停下載進程 |
unBindServiceIfIdle(void) | 若是目前下載進程沒有任務正在執行,則關停下載進程 |
isServiceConnected(void) | 是否已經啓動而且鏈接上下載進程(可參考任務管理demo中的使用) |
getStatusIgnoreCompleted(downloadId) | 獲取不包含已完成狀態的下載狀態(若是任務已經下載完成,將收到INVALID ) |
getStatus(id:int, path:String) | 獲取下載狀態 |
getStatus(url:String, path:String) | 獲取下載狀態 |
setGlobalPost2UIInterval(intervalMillisecond:int) | 爲了不掉幀,這裏是設置了最多每interval毫秒拋一個消息到ui線程(使用Handler),防止因爲回調的過於頻繁致使ui線程被ddos致使掉幀。 默認值: 10ms. 若是設置小於0,將會失效,也就是說每一個回調都直接拋一個消息到ui線程 |
setGlobalHandleSubPackageSize(packageSize:int) | 爲了不掉幀, 若是上面的方法設置的間隔是一個小於0的數,這個packageSize將不會生效。packageSize這個值是爲了不在ui線程中一次處理過多回調,結合上面的間隔,就是每一個interval毫秒間隔拋一個消息到ui線程,而每一個消息在ui線程中處理packageSize個回調。默認值: 5 |
enableAvoidDropFrame(void) | 開啓 避免掉幀處理。就是將拋消息到ui線程的間隔設爲默認值10ms, 很明顯會影響的是回調不會立馬通知到監聽器(FileDownloadListener)中,默認值是: 最多10ms處理5個回調到監聽器中 |
disableAvoidDropFrame(void) | 關閉 避免掉幀處理。就是將拋消息到ui線程的間隔設置-1(無效值),這個就是讓每一個回調都會拋一個消息ui線程中,可能引發掉幀 |
isEnabledAvoidDropFrame(void) | 是否開啓了 避免掉幀處理。默認是開啓的 |
startForeground(id:int, notification:Notification) | 設置FileDownloadService爲前臺模式,保證用戶從最近應用列表移除應用之後下載服務不會被殺 |
stopForeground(removeNotification:boolean) | 取消FileDownloadService的前臺模式 |
setTaskCompleted(url:String, path:String, totalBytes:long) | 用於告訴FileDownloader引擎,以指定Url與Path的任務已經經過其餘方式(非FileDownloader)下載完成 |
setTaskCompleted(taskAtomList:List) | 用於告訴FileDownloader引擎,指定的一系列的任務都已經經過其餘方式(非FileDownloader)下載完成 |
setMaxNetworkThreadCount(int) | 設置最大並行下載的數目(網絡下載線程數), [1,12] |
clearAllTaskData() | 清空filedownloader 數據庫中的全部數據 |
InitCustomMaker
)方法名 | 需實現接口 | 已有組件 | 默認組件 | 說明 |
---|---|---|---|---|
database | FileDownloadDatabase | DefaultDatabaseImpl | DefaultDatabaseImpl | 傳入定製化數據庫組件,用於存儲用於斷點續傳的數據 |
connection | FileDownloadConnection | FileDownloadUrlConnection | FileDownloadUrlConnection | 傳入定製化的網絡鏈接組件,用於下載時創建網絡鏈接 |
outputStreamCreator | FileDownloadOutputStream | FileDownloadRandomAccessFile、FileDownloadBufferedOutputStream、FileDownloadOkio | FileDownloadRandomAccessFile | 傳入輸出流組件,用於下載時寫文件使用 |
maxNetworkThreadCount | - | - | 3 | 傳入建立下載引擎時,指定可用的下載線程個數 |
若是你但願Okhttp做爲你的網絡鏈接組件,可使用這個庫。git
方法名 | 備註 |
---|---|
setPath(path:String) | 下載文件的存儲絕對路徑 |
setPath(path:String, pathAsDirectory:boolean) | 若是pathAsDirectory 是true ,path 就是存儲下載文件的文件目錄(而不是路徑),此時默認狀況下文件名filename 將會默認從response#header 中的contentDisposition 中得到 |
setListener(listener:FileDownloadListener) | 設置監聽,能夠以相同監聽組成隊列 |
setCallbackProgressTimes(times:int) | 設置整個下載過程當中FileDownloadListener#progress 最大回調次數 |
setCallbackProgressIgnored() | 忽略全部的FileDownloadListener#progress 的回調 |
setCallbackProgressMinInterval(minIntervalMillis:int) | 設置每一個FileDownloadListener#progress 之間回調間隔(ms) |
setTag(tag:Object) | 內部不會使用,在回調的時候用戶本身使用 |
setTag(key:int, tag:Object) | 用於存儲任意的變量方便回調中使用,以key做爲索引 |
setForceReDownload(isForceReDownload:boolean) | 強制從新下載,將會忽略檢測文件是否健在 |
setFinishListener(listener:FinishListener) | 結束監聽,僅包含結束(over(void))的監聽 |
setAutoRetryTimes(autoRetryTimes:int) | 當請求或下載或寫文件過程當中存在錯誤時,自動重試次數,默認爲0次 |
setSyncCallback(syncCallback:boolean) | 若是設爲true, 全部FileDownloadListener中的回調都會直接在下載線程中回調而不拋到ui線程, 默認爲false |
addHeader(name:String, value:String) | 添加自定義的請求頭參數,須要注意的是內部爲了斷點續傳,在判斷斷點續傳有效時會自動添加上(If-Match 與Range 參數),請勿重複添加致使400或其餘錯誤 |
addHeader(line:String) | 添加自定義的請求頭參數,須要注意的是內部爲了斷點續傳,在判斷斷點續傳有效時會自動添加上(If-Match 與Range 參數),請勿重複添加致使400或其餘錯誤 |
setMinIntervalUpdateSpeed(minIntervalUpdateSpeedMs:int) | 設置下載中刷新下載速度的最小間隔 |
removeAllHeaders(name:String) | 刪除由自定義添加上去請求參數爲{name} 的全部鍵對 |
setWifiRequired(isWifiRequired:boolean) | 設置任務是否只容許在Wifi網絡環境下進行下載。 默認值false |
asInQueueTask(void):InQueueTask | 申明該任務將會是隊列任務中的一個任務,而且轉化爲InQueueTask ,以後能夠調用InQueueTask#enqueue 將該任務入隊以便於接下來啓動隊列任務時,能夠將該任務收編到隊列中 |
start(void) | 啓動孤立的下載任務 |
pause(void) | 暫停下載任務(也能夠理解爲中止下載,可是在start的時候默認會斷點續傳) |
getId(void):int | 獲取惟一Id(內部經過url與path生成) |
getUrl(void):String | 獲取下載鏈接 |
getCallbackProgressTimes(void):int | 得到progress最大回調次數 |
getCallbackProgressMinInterval(void):int | 得到每一個progress之間的回調間隔(ms) |
getPath(void):String | 獲取文件路徑 或 文件目錄 |
isPathAsDirectory | 判斷getPath() 返回的路徑是文件存儲目錄(directory ),仍是文件存儲路徑(directory/filename ) |
getTargetFilePath | 獲取目標文件的存儲路徑 |
getListener(void):FileDownloadListener | 獲取監聽器 |
getSoFarBytes(void):int | 獲取已經下載的字節數 |
getTotalBytes(void):int | 獲取下載文件總大小 |
getStatus(void):int | 獲取當前的狀態 |
isForceReDownload(void):boolean | 是否強制從新下載 |
getEx(void):Throwable | 獲取下載過程拋出的Throwable |
isReusedOldFile(void):boolean | 判斷是不是直接使用了舊文件(檢測是有效文件),沒有啓動下載 |
getTag(void):Object | 獲取用戶setTag進來的Object |
getTag(key:int):Object | 根據key獲取存儲在task中的變量 |
isContinue(void):boolean | 是否成功斷點續傳 |
getEtag(void):String | 獲取當前下載獲取到的ETag |
getAutoRetryTimes(void):int | 自動重試次數 |
getRetryingTimes(void):int | 當前重試次數。將要開始重試的時候,會將接下來是第幾回 |
isSyncCallback(void):boolean | 是不是設置了全部FileDownloadListener中的回調都直接在下載線程直接回調而不拋到ui線程 |
getSpeed():int | 獲取任務的下載速度, 下載過程當中爲實時速度,下載結束狀態爲平均速度 |
isUsing():boolean | 判斷當前的Task對象是否在引擎中啓動過 |
isWifiRequired():boolean | 獲取當前任務是否被設置過只容許在Wifi網絡環境下下載 |
FileDownloadListener
)說明pending -> started -> connected -> (progress <->progress) -> blockComplete -> completed
paused / completed / error / warn
isReusedOldFile
進行決策是不是該狀況)(也能夠經過setForceReDownload(true)
來避免該狀況):blockComplete -> completed
回調方法 | 備註 | 帶回數據 |
---|---|---|
pending | 等待,已經進入下載隊列 | 數據庫中的soFarBytes與totalBytes |
started | 結束了pending,而且開始當前任務的Runnable | - |
connected | 已經鏈接上 | ETag, 是否斷點續傳, soFarBytes, totalBytes |
progress | 下載進度回調 | soFarBytes |
blockComplete | 在完成前同步調用該方法,此時已經下載完成 | - |
retry | 重試以前把將要重試是第幾回回調回來 | 之因此重試遇到Throwable, 將要重試是第幾回, soFarBytes |
completed | 完成整個下載過程 | - |
paused | 暫停下載 | soFarBytes |
error | 下載出現錯誤 | 拋出的Throwable |
warn | 在下載隊列中(正在等待/正在下載)已經存在相同下載鏈接與相同存儲路徑的任務 | - |
FileDownloadListener
中的方法回調過快,致使掉幀?你有兩種方法能夠解決這個問題github
FileDownloader#enableAvoidDropFrame
, 默認 就是開啓的BaseDownloadTask#setSyncCallback
, 默認是false, 若是設置爲true,全部的回調都會在下載線程直接同步調用而不會拋到ui線程。FileDownloadMonitor
你能夠添加一個全局監聽器來進行打點或者是調試數據庫
方法名 | 備註 |
---|---|
setGlobalMonitor(monitor:IMonitor) | 設置與替換一個全局監聽器到下載引擎中 |
releaseGlobalMonitor(void) | 釋放已經設置到下載引擎中的全局監聽器 |
getMonitor(void) | 獲取已經設置到下載引擎中的全局監聽器 |
FileDownloadMonitor.IMonitor
監聽器接口類緩存
接口 | 備註 |
---|---|
onRequestStart(count:int, serial:boolean, lis:FileDownloadListener) | 將會在啓動隊列任務是回調這個方法 |
onRequestStart(task:BaseDownloadTask) | 將會在啓動單一任務時回調這個方法 |
onTaskBegin(task:BaseDownloadTask) | 將會在內部接收並開始task的時候回調這個方法(會在pending 回調以前) |
onTaskStarted(task:BaseDownloadTask) | 將會在task結束pending開始task的runnable的時候回調該方法 |
onTaskOver(task:BaseDownloadTask) | 將會在task走完全部生命週期是回調這個方法 |
FileDownloadUtils
方法名 | 備註 |
---|---|
setDefaultSaveRootPath(path:String) | 在整個引擎中沒有設置路徑時BaseDownloadTask#setPath 這個路徑將會做爲它的Root path |
getTempPath | 獲取用於存儲還未下載完成文件的臨時存儲路徑: filename.temp |
isFilenameConverted(context:Context) | 判斷是否全部數據庫中下載中的任務的文件名都已經從filename (在舊架構中)轉爲filename.temp |
FileDownloadNotificationHelper
如何快速集成Notification呢? 建議參考NotificationMinSetActivity、NotificationSampleActivity。安全
filedownloader.properties
若是你須要定製化FileDownloader,能夠在你的項目模塊的
assets
目錄下添加 'filedownloader.properties' 文件(如/demo/src/main/assets/filedownloader.properties
),而後添加如下可選相關配置。網絡
格式:
keyword=value
架構
關鍵字 | 描述 | 默認值 |
---|---|---|
http.lenient | 若是你遇到了: 'can't know the size of the download file, and its Transfer-Encoding is not Chunked either', 可是你想要忽略相似的返回頭不規範的錯誤,直接將該關鍵字參數設置爲true 便可,咱們將會將其做爲chunck 進行處理 |
false |
process.non-separate | FileDownloadService 默認是運行在獨立進程':filedownloader'上的, 若是你想要FileDownloadService共享並運行在主進程上, 將該關鍵字參數設置爲true ,能夠有效減小IPC產生的I/O |
false |
download.min-progress-step | 最小緩衝大小,用於斷定是不是時候將緩衝區中進度同步到數據庫,以及是不是時候要確保下緩存區的數據都已經寫文件。值越小,更新會越頻繁,下載速度會越慢,可是應對進程被沒法預料的狀況殺死時會更加安全 | 65536 |
download.min-progress-time | 最小緩衝時間,用於斷定是不是時候將緩衝區中進度同步到數據庫,以及是不是時候要確保下緩存區的數據都已經寫文件。值越小,更新會越頻繁,下載速度會越慢,可是應對進程被沒法預料的狀況殺死時會更加安全 | 2000 |
download.max-network-thread-count | 用於同時下載的最大網絡線程數, 區間[1, 12] | 3 |
file.non-pre-allocation | 是否不須要在開始下載的時候,預申請整個文件的大小(content-length ) |
false |
III. 異常處理
全部的異常,都將在
FileDownloadListener#error(BaseDownloadTask, Throwable)
中獲知。
Exception | 緣由 |
---|---|
FileDownloadHttpException |
在發出請求之後,response-code不是200(HTTP_OK),也不是206(HTTP_PARTIAL)的狀況下會拋出該異常; 在這個異常對象會帶上 response-code、response-header、request-header。 |
FileDownloadGiveUpRetryException |
在請求返回的 response-header 中沒有帶有文件大小(content-length),而且不是流媒體(transfer-encoding)的狀況下會拋出該異常;出現這個異常,將會忽略全部重試的機會(BaseDownloadTask#setAutoRetryTimes ). 你能夠經過在filedownloader.properties 中添加 http.lenient=true 來忽略這個異常,而且在該狀況下,直接做爲流媒體進行下載。 |
FileDownloadOutOfSpaceException |
當將要下載的文件大小大於剩餘磁盤大小時,會拋出這個異常。 |
其餘 | 程序錯誤。 |
FileDownloadNetworkPolicyException |
設置了BaseDownloadTask#setWifiRequired(true) ,在下載過程當中,一旦發現網絡狀況轉爲非Wifi環境,便會拋回這個異常 |
PathConflictException |
當有一個正在下載的任務,它的存儲路徑與當前任務的存儲路徑徹底一致,爲了不多個任務對同一個文件進行寫入,當前任務便會拋回這個異常 |
這邊的數據並很少,只是一些隊列數據,用不了多少內存。
若是在前臺的時候這個數據都被回收了, 你的應用應該也掛了。極低機率事件。
通常事件, 若是是你的下載是UI進程啓動的,若是你的UI進程處於後臺進程
(能夠理解爲應用被退到後臺)狀態,在內存不足的狀況下會被回收(回收優先級高於服務進程
),此時分兩種狀況:
是串行隊列任務,在回收掉UI進程內存之後,下載進程會繼續下載完已經pending到下載進程的那個任務,而還未pending到下載進程的任務會中斷下載(因爲任務驅動線性執行的是在UI進程); 有損體驗: 下次進入應用重啓啓動整個隊列,會繼續上次的下載。
是並行隊列任務,在回收掉UI進程內存之後,下載進程會繼續下載全部任務(全部已經pending到下載進程的任務,因爲這裏的pending速度是很快的,所以幾乎是點擊並行下載,全部任務在很短的時間內都已經pending到下載進程了),而UI進程因爲被回收,將不會收到全部的監聽; 有損體驗: 下次進入應用從新啓動整個隊列,就會和正常的下載啓動一致,收到全部狀況的監聽。
對內存有必定的佔用,可是並很少,每次啓動進程會根據數據的有效性進行清理冗餘數據,被回收是低機率事件
因爲下載不斷有不一樣的buffer佔用內存,可是因爲在下載時,是活躍的服務進程
,所以被回收是低機率事件(會先回收完全部空進程
、後臺進程
(後臺應用)之後,若是內存還不夠,纔會回收該進程)。
即便被回收,也不會有任何問題。因爲咱們使用的是START_STICKY
(若是不但願被重啓可主動調用FileDownloader#unBindService
/FileDownloader#unBindServiceIfIdle
),所以在內存足夠的時候,下載進程會嘗試重啓(系統調度),非下載進程(通常是UI進程) 接收到下載進程的鏈接,會繼續下載與繼續接收回調,下載進程也會斷點續傳沒有下載完的全部任務(不管並行與串行),不會影響體驗。