原文地址: https://www.shanya.world/archives/6f11b78c.html
爲了更好的方便使用,我已經上傳至jitpack
,直接在添加依賴即可。
源碼地址: https://github.com/Shanyaliux/DownloadUtil
使用方法
- 根目錄的
build.gradle
添加maven { url 'https://jitpack.io' }
, 位置如下
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
- 添加依賴
implementation 'com.github.Shanyaliux:DownloadUtil:1.0.0'
- 調用方法
/**
* 下載工具的創建
*@param Url 網絡資源路徑
*@param Path 本地存儲路徑
*@param ThreadNum 下載線程數量 (若服務器請求數據沒有Content-Type,則默認單線程顯示,且無法監聽下載進度)
*@param new DownloadUtil.OnDownloadListener() 下載狀態監聽接口
*/
DownloadUtil downloadUtil = DownloadUtil.getInstance(Url, Path, ThreadNum, new DownloadUtil.OnDownloadListener() {
@Override
public void onDownloadSuccess() {
//下載成功
}
@Override
public void onDownloadProgress(int progress) {
//下載進度(若服務器請求數據沒有Content-Type,其無效)
}
@Override
public void onDownloadFailed(Exception e) {
//下載失敗
}
});
/**
* 下載開始函數 (目前建議單獨放一個線程執行)
*/
new Thread(() -> {
downloadUtil.download();
}).start();
具體源碼
- 添加
OkHttp
依賴
implementation("com.squareup.okhttp3:okhttp:4.5.0")
DownLoadUtil.java
package com.shanya.downloadutil;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class DownloadUtil {
//資源路徑
private String path;
//文件保存位置
private String targetFile;
//下載線程數量(當請求來的Content-Length爲null時默認單線程下載)
private int threadNum;
//下載線程對象
private DownloadThread[] threads;
//文件大小(當請求來的Content-Length爲null 其沒有用)
private int fileSize;
//每個線程已下載數據存儲,用來計算進度
private int[] lengths;
//監聽下載狀態
private OnDownloadListener onDownloadListener;
private static final String TAG = "DownloadUtil";
//Singleton單例化
private static DownloadUtil downloadUtil;
public static DownloadUtil getInstance(String path, String targetFile, int threadNum, OnDownloadListener onDownloadListener){
if (downloadUtil == null) {
downloadUtil = new DownloadUtil(path, targetFile, threadNum,onDownloadListener);
}
return downloadUtil;
}
//構造函數
private DownloadUtil(final String path, final String targetFile, final int threadNum, final OnDownloadListener onDownloadListener) {
this.path = path;
this.targetFile = targetFile;
this.threadNum = threadNum;
threads = new DownloadThread[threadNum];
lengths = new int[threadNum];
this.onDownloadListener = onDownloadListener;
}
//下載
public void download() {
//創建OkHttpClient實例
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.build();
okhttp3.Request request = new Request.Builder()
.get()
.url(path)
.build();
Call call = client.newCall(request);
//異步請求
call.enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
//下載失敗回調
onDownloadListener.onDownloadFailed(e);
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
//判斷連接是否成功
if (response.code() == HttpURLConnection.HTTP_OK) {
Headers headers = response.headers();
//打印請求來的一些信息
// for (int i = 0; i < headers.size(); i++) {
// Log.d(TAG, "onResponse: ----> " + headers.name(i) + " === " + headers.value(i));
// }
//獲取文件大小
if (headers.get("Content-Length") != null){
fileSize = Integer.parseInt(headers.get("Content-Length"));
//根據線程數分解每個線程需下載的文件大小
int currentPartSize = fileSize / threadNum + 1;
//創建RandomAccessFile 填寫“rw” 沒有文件會自動創建
RandomAccessFile file = new RandomAccessFile(targetFile, "rw");
//設置本地文件大小
file.setLength(fileSize);
file.close();
for (int i = 0; i < threadNum; i++) {
//每條線程使用一個RandomAccessFile 進行下載
RandomAccessFile currentPart = new RandomAccessFile(targetFile, "rw");
//計算每條線程下載的開始位置
int startPos = i * currentPartSize;
//定位該線程的下載位置
currentPart.seek(startPos);
//創建下載線程
threads[i] = new DownloadThread(i,startPos, currentPartSize, currentPart);
//啓動下載線程
threads[i].start();
}
}else{//讀取不到文件長度則執行以下內容
fileSize = -1;
//創建本地文件
File outFile = new File(targetFile);
//判斷文件是否存在 不存在則新建
if (!outFile.getParentFile().exists()) {
outFile.mkdirs();
}
if (!outFile.exists()) {
outFile.createNewFile();
}
//創建文件輸出流
FileOutputStream fos = new FileOutputStream(outFile);
//創建輸入流
InputStream inputStream = null;
//判斷請求數據是否爲null
if (response.body() != null) {
//獲取InputStream實例
inputStream = response.body().byteStream();
byte[] buffer = new byte[1024];
int len;
//讀取網絡數據
while ((len = inputStream.read(buffer, 0, buffer.length)) != -1) {
fos.write(buffer, 0, len);
}
//寫入文件
fos.flush();
}
fos.close();
inputStream.close();
//下載成功回調
onDownloadListener.onDownloadSuccess();
}
}
}
});
}
/**
* //獲取下載百分比(若沒有請求到文件大小 則不會調用該方法)
* @return 下載進度
*/
private double getCompleteRate(){
int sumSize = 0;
for (int i = 0; i < threadNum; i++) {
sumSize += lengths[i];
}
return sumSize * 1.0 / fileSize;
}
/**
* 下載線程
*/
private class DownloadThread extends Thread{
//該線程開始下載的位置
private int startPos;
//該線程負責的文件大小
private int currentPartSize;
//該線程下載使用的 RandomAccessFile
private RandomAccessFile currentPart;
//線程序號,計算下載進度時需要
int num;
//構造函數
DownloadThread(int num,int startPos, int currentPartSize, RandomAccessFile currentPart) {
this.num = num;
this.startPos = startPos;
this.currentPartSize = currentPartSize;
this.currentPart = currentPart;
}
@Override
public void run() {
super.run();
//OkHttpClient 實例
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.build();
okhttp3.Request request = new Request.Builder()
.get()
.url(path)
.build();
Call call = client.newCall(request);
try {
//同步請求
Response execute = call.execute();
int code = execute.code();
//判斷是否連接成功
if (code == HttpURLConnection.HTTP_OK) {
InputStream inputStream = null;
if (execute.body() != null) {
inputStream = execute.body().byteStream();
//跳過 startPos 個字節,表明該線程只下載自己負責的那部分
inputStream.skip(startPos);
byte[] buffer = new byte[1024];
int len;
//讀取網絡數據,並寫入本地文件中
while (lengths[num] < currentPartSize && (len = inputStream.read(buffer)) > 0) {
currentPart.write(buffer, 0, len);
//累計該線程下載的總大小
lengths[num] += len;
//更新進度條
if (getCompleteRate() >= 1.0){
onDownloadListener.onDownloadSuccess();
}
onDownloadListener.onDownloadProgress((int)(getCompleteRate() * 100));
}
}
currentPart.close();
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 下載狀態回調接口
*/
public interface OnDownloadListener{
//下載成功
void onDownloadSuccess();
//更新進度
void onDownloadProgress(int progress);
//下載失敗
void onDownloadFailed(Exception e);
}
}
源碼地址: https://github.com/Shanyaliux/DownloadUtil
如有不足之處請多多指正,謝謝