封裝一個 OkHttp 的下載工具類

原文地址: https://www.shanya.world/archives/6f11b78c.html

爲了更好的方便使用,我已經上傳至jitpack,直接在添加依賴即可。

如何上傳 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

如有不足之處請多多指正,謝謝

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章