封装一个 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

如有不足之处请多多指正,谢谢

你可能感兴趣的:(封装一个 OkHttp 的下载工具类)