完整的OKhttp请求封装的网络框架

完整的OKhttp请求封装的网络框架

1.实现get,post等请求
2.支持上传,下载功能(支持断点续传)
3.支持Gson泛型解析json数据
4.使用mvc设计模式
5.封装glide图片库使用
6.支持自定义异常,异常的集中处理
7.添加缓存支持
8.支持log显示控制
9.支持文件上传下载

具体功能科参考项目中的网络框架使用demo和下载demo

下面针对断点续传的核心类,跟大家分享下,不对的地方还望指导;

实现思路:
使用OKHttp发送下载请求,通过下载管理器中的线程池控制多个线程,同时下载,默认支持5个任务。
在用户发送暂停下载命令时,结束当前下载线程,并保存当前下载任务的状态和下载已完成的大小。继续下载时,重新启动线程放入线程池中,开始下载前从数据库中获取对已的下载文件大小和状态,实现断点续传,继续下载,保存到文件中。

如以下代码中注释,描述实现过程:

public class DownLoadTask implements Runnable {

        private static String FILE_MODE = "rwd";
        private OkHttpClient mClient;
        private Call call;

        // 便于对跳转到文件的指定位置,进行读写操作
        private RandomAccessFile mDownLoadFile;
        // 这里使用greendao保存数据
        private DownloadEntity dbEntity;
        private DownloadDao mDownloadDao;
        private DownloadTaskListener mListener;

        // 包装传递下载要使用的信息
        private Builder mBuilder;
        private String id;// task id
        private long totalSize;// filesize
        private long completedSize; //  Download section has been completed
        private String url;// file url
        private String saveDirPath;// file save path
        private String fileName; // File name when saving
        private int downloadStatus;

        // 下载百分百
        private int percent;
        // 下载失败code
        private int errorCode;

        private DownLoadTask(Builder builder) {
            this.mBuilder = builder;
            this.id = mBuilder.id;
            this.url = mBuilder.url;
            this.saveDirPath = mBuilder.saveDirPath;
            this.fileName = mBuilder.fileName;
            this.downloadStatus = mBuilder.downloadStatus;
            this.mListener = mBuilder.listener;
        }

        @Override
        public void run() {
            try {
                // 数据库中加载数据,不为空说明下载过(可能未下载完成)
                dbEntity = mDownloadDao.load(id);
                if (dbEntity != null) {
                    completedSize = dbEntity.getCompletedSize();
                    totalSize = dbEntity.getTotalSize();
                }

                // 获得文件保存路径
                String filepath = getDownLoadFilePath().getAbsolutePath();
                // 获得下载保存文件
                mDownLoadFile = new RandomAccessFile(filepath, FILE_MODE);

                // 获得本地下载的文件大小
                long fileLength = mDownLoadFile.length();
                // 本地文件的大小和数据库中保存的文件大小相同,说明下载完成,直接返回
                if (fileLength > 0 && totalSize == fileLength) {
                    downloadStatus = DownloadStatus.DOWNLOAD_STATUS_COMPLETED;
                    completedSize = fileLength;
                    totalSize = fileLength;
                    percent = 100;
                    WLog.d("totalSize=" + totalSize + " ,completedSize=" + completedSize + " ,downloadStatus="
                        + downloadStatus);
                    // 执行回调,通知界面层
                    onCallBack();
                    return;
                }

                // ============没有下过或下载为完成,则开始下载==================
                Request request = new Request.Builder().url(url)//
                    .header("RANGE", "bytes=" + completedSize + "-") // Http value set breakpoints RANGE
                    .build();
                // 文件跳转到指定位置开始写入
                mDownLoadFile.seek(completedSize);
                // 创建一个下载请求
                call = mClient.newCall(request);
                call.enqueue(new Callback() {
                    @Override
                    public void onFailure(Call call, IOException e) {
                        // 发送错误事件 
                        error();
                    }

                    @Override
                    public void onResponse(Call call, Response response) throws IOException {
                        ResponseBody body = response.body();
                        if (body != null) {
                            // 更新回调下载状态,初始化为正在下载
                            downloadStatus = DownloadStatus.DOWNLOAD_STATUS_DOWNLOADING;
                            // 回调更新界面层
                            onCallBack();
                            // 开始下载
                            readAndSave2File(body.byteStream(), body.contentLength());
                        }
                    }
                });
            } catch (FileNotFoundException e) {
                // file not found
                WLog.e(e.getMessage());
                error();
            } catch (IOException e) {
                // io exception
                WLog.e(e.getMessage());
                error();
            }
        }

        // 读取下载流,并保存到文件中
        private void readAndSave2File(InputStream inputStream, final long contentLength) throws IOException {
            byte[] buf = new byte[2048];
            int length = 0;
            try {
                long sum = completedSize;
                float times = 0;

                // contentLength 在暂停后再次启动会从断点后开始计算,会小于文件原始的大小
                // 所以只能在第一次加载的时候保存获取文件大小,否则计算百分百会不对
                if (totalSize <= 0) {
                    totalSize = contentLength;
                }

                while ((length = inputStream.read(buf)) != -1) {
                    // 如果不是正在下载状态,则停止任务,结束下载
                    if (downloadStatus != DownloadStatus.DOWNLOAD_STATUS_DOWNLOADING) {
                        return;
                    }

                    sum += length;
                    mDownLoadFile.write(buf, 0, length);

                    completedSize = sum;
                    // 计算百分比
                    float prgress = (float) (completedSize * 100 / totalSize);
                    // WLog.d("completedSize=" + completedSize + " ,prgress=" + prgress);
                    // 防止频繁的更新界面,控制更新进度,回调100次
                    if (prgress - times >= 1) {
                        times = prgress;
                        // 更新状态
                        downloadStatus = DownloadStatus.DOWNLOAD_STATUS_DOWNLOADING;
                        // 更新百分比
                        percent = (int) prgress;
                        onCallBack();
                    }
                }
            } catch (Exception e) {
                WLog.e(e.toString());
            } finally {
                // 任务结束,最终都会执行的地方
                WLog.d("completedSize=" + completedSize);
                // 回收资源
                close(inputStream);
                close(mDownLoadFile);

                // 判断是否下载完成,更新下载状态
                isDownloadFinish();
                // 最终都会执行,数据库更新
                insertOrUpdateDB();
                // 回调一次
                onCallBack();
            }
        }

        // 判断文件下载是否完成(下载的大小是否与文件大小相等)
        private boolean isDownloadFinish() {
            boolean finish = false;
            if (totalSize > 0 && completedSize > 0 && totalSize == completedSize) {
                downloadStatus = DownloadStatus.DOWNLOAD_STATUS_COMPLETED;
                finish = true;
            }
            return finish;
        }

        /**
         * 更新数据库操作
         */
        private void insertOrUpdateDB() {
            if (dbEntity == null) {
                dbEntity = new DownloadEntity(id, totalSize, completedSize, url, saveDirPath, fileName, downloadStatus);
            }
            else {
                dbEntity.setCompletedSize(completedSize);
                dbEntity.setDownloadStatus(downloadStatus);
            }

            mDownloadDao.insertOrReplace(dbEntity);

            WLog.d("totalSize=" + dbEntity.getTotalSize() + " ,completedSize=" + dbEntity.getCompletedSize()
                + " ,downloadStatus=" + dbEntity.getDownloadStatus());
        }

        /**
         * error 事件处理
         */
        private void error() {
            downloadStatus = DownloadStatus.DOWNLOAD_STATUS_ERROR;
            errorCode = DownloadStatus.DOWNLOAD_ERROR_IO_ERROR;
            onCallBack();
        }

        /**
         * 取消下载
         */
        public void cancel() {
            downloadStatus = DownloadStatus.DOWNLOAD_STATUS_CANCEL;
            doCancelClear();
            cancelRequest();
            onCallBack();
        }

        /**
         * 暂停下载
         */
        public void pause() {
            downloadStatus = DownloadStatus.DOWNLOAD_STATUS_PAUSE;
            onCallBack();
        }

        /**
         * 取消下载请求
         */
        private void cancelRequest() {
            if (call != null) {
                if (!call.isCanceled()) {
                    call.cancel();
                }
            }
        }

        /**
         * 删除本地文件和数据库数据
         */
        private void doCancelClear() {
            if (dbEntity != null) {
                mDownloadDao.delete(dbEntity);
                File temp = getDownLoadFilePath();
                if (temp.exists()) {
                    temp.delete();
                }
            }
        }

        /**
         * 分发回调事件到ui层
         */
        private void onCallBack() {
            // 发送当前下载状态
            mHandler.sendEmptyMessage(downloadStatus);
            // 同步manager中的task信息
            DownloadManager.getInstance().updateDownloadTask(this);
        }

        Handler mHandler = new Handler(Looper.getMainLooper()) {
            @Override
            public void handleMessage(Message msg) {
                int code = msg.what;
                switch (code) {
                    // 下载失败
                    case DownloadStatus.DOWNLOAD_STATUS_ERROR:
                        mListener.onError(DownLoadTask.this, errorCode);
                        break;
                    // 正在下载
                    case DownloadStatus.DOWNLOAD_STATUS_DOWNLOADING:
                        mListener.onDownloading(DownLoadTask.this, completedSize, totalSize, percent);
                        break;
                    // 取消
                    case DownloadStatus.DOWNLOAD_STATUS_CANCEL:
                        mListener.onCancel(DownLoadTask.this);
                        break;
                    // 完成
                    case DownloadStatus.DOWNLOAD_STATUS_COMPLETED:
                        mListener.onDownloadSuccess(DownLoadTask.this, getDownLoadFilePath());
                        break;
                    // 停止
                    case DownloadStatus.DOWNLOAD_STATUS_PAUSE:
                        mListener.onPause(DownLoadTask.this, completedSize, totalSize, percent);
                        break;
                }
            }
        };

        // 获得文件保存路径
        private File getDownLoadFilePath() {
            File file = FileUtil.getDownLoadDir();
            String parentDir = file.getAbsolutePath();
            // 获得文件名
            String filename = !TextUtils.isEmpty(fileName) ? fileName : getFileNameFromUrl(url);
            File downFile = new File(parentDir, filename);
            return downFile;
        }

        @Override
        public boolean equals(Object obj) {
            if(obj instanceof DownLoadTask){
                DownLoadTask task = (DownLoadTask) obj;
                return task.getId().equals(this.getId());
            }
            return super.equals(obj);
        }

        //=============================================================

        public void setDownloadDao(DownloadDao mDownloadDao) {
            this.mDownloadDao = mDownloadDao;
        }

        public Builder getBuilder() {
            return mBuilder;
        }

        public void setBuilder(Builder builder) {
            this.mBuilder = builder;
        }

        public String getId() {
            return id;
        }

        public String getUrl() {
            return url;
        }

        public String getSaveDirPath() {
            return saveDirPath;
        }

        public String getFileName() {
            return fileName;
        }

        public void setClient(OkHttpClient mClient) {
            this.mClient = mClient;
        }

        public void setTotalSize(long totalSize) {
            this.totalSize = totalSize;
        }

        public void setCompletedSize(long completedSize) {
            this.completedSize = completedSize;
        }

        public void setDownloadStatus(int downloadStatus) {
            this.downloadStatus = downloadStatus;
        }

        public int getDownloadStatus() {
            return downloadStatus;
        }

        public static class Builder {
            private String id;// task id
            private String url;// file url
            private String saveDirPath;// file save path
            private String fileName; // File name when saving
            private int downloadStatus = DownloadStatus.DOWNLOAD_STATUS_INIT;

            private DownloadTaskListener listener;

            /**
             * 作为下载task开始、删除、停止的key值(not null)
             *
             * @param id
             * @return
             */
            public Builder setId(String id) {
                this.id = id;
                return this;
            }

            /**
             * 下载url(not null)
             *
             * @param url
             * @return
             */
            public Builder setUrl(String url) {
                this.url = url;
                return this;
            }

            /**
             * 设置保存地址
             *
             * @param saveDirPath
             * @return
             */
            public Builder setSaveDirPath(String saveDirPath) {
                this.saveDirPath = saveDirPath;
                return this;
            }

            /**
             * 设置下载状态
             *
             * @param downloadStatus
             * @return
             */
            public Builder setDownloadStatus(int downloadStatus) {
                this.downloadStatus = downloadStatus;
                return this;
            }

            /**
             * 设置文件名
             *
             * @param fileName
             * @return
             */
            public Builder setFileName(String fileName) {
                this.fileName = fileName;
                return this;
            }

            /**
             * 设置下载回调
             *
             * @param listener
             * @return
             */
            public Builder setListener(DownloadTaskListener listener) {
                this.listener = listener;
                return this;
            }

            public DownLoadTask build() {
                return new DownLoadTask(this);
            }
        }

    }

这里写图片描述

这里写图片描述

源码下载地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值