Android断点下载RandomAccessFile实现

摘要:本文实现断点下载文件,支持多调线程,最大数量10条。(下载测试用例为APK文件,保证了文件的正确性。)

            github已经托管项目,可以下载demo查看,同事提供在线引用,使用方式参考、:

https://github.com/yuanhy/Appframework

源码:https://github.com/yuanhy/AppframeworkLib/blob/master/library_tools/src/main/java/com/yuanhy/library_tools/file/FileDownUtile.java

下面是一些主要的代码摘要:

/**
 * 断点下载的文件
 *
 * @param context          上下文
 * @param multithreadnubs  线程数量 (最大10条)
 * @param url              网络url
 * @param fileDownPath     文件的父目录
 * @param progressListener
 */
public static void fileDownMultithreadBreakpoint();一行代码就可以达到你要的要求。不用再去管里面的业务。如果你想了解更多,就继续往下。
    AndroidOkHttp3 androidOkHttp3 = AndroidOkHttp3.getInstance(context);
    androidOkHttp3.initFileClient();
1    //获取文件的长度
    androidOkHttp3.getContentLength(url, new YCallBack() {
        @Override
        public void onError(Object o) {
            progressListener.onError("");
        }

        @Override
        public void onOk(Object o) {
            HashMap<String, Long> hashMap = new HashMap<>();
            long contentlength = (long) o;
            long length = (contentlength / multithreadnubs);
            if (contentlength > 0) {//2获取到正确的文件长度
                for (int i = 0; i < multithreadnubs; i++) {//线程分割
                    long start = length * i;
                    long end = length * i + length - 1;
                    if (i == multithreadnubs - 1) {//3最后一条线程要特别处理
                        start = length * i;
                        end = contentlength - 1;
                    }
                    String dataKey = fileName + start;//4多点下载保存本节点的key,用于实现  界面的进度刷新功能
                    long them = SharedPreferencesUtil.getSharedPreferencesUtil(context).getLong(dataKey);
                    hashMap.put(dataKey, them);
                    AppFramentUtil.logCatUtil.i("总长度:" + contentlength +
                            " 线程 :" + i + "起始位置:" + start + "  结束位置:" + end);
//5下载文件的线程
                    FileDownThred fileDownThred = new FileDownThred(context, start, end, url, fileDownPath,
                            fileName, i, new ProgressListener() {

                        @Override
                        public void onProgress(long fileSizeBytes, long remainingBytes, String dataKey, String thredId, boolean done) {
                            long downFileSize = remainingBytes;
                            hashMap.put(dataKey, downFileSize);
                            long lenth = 0;
                            for (long value : hashMap.values()) {
                                lenth = lenth + value;
                            }
                            int progress = (int) (lenth * 100 / contentlength);

                            AppFramentUtil.logCatUtil.i("线程数量:" + multithreadnubs +
                                    " 当前线程:" + thredId +
                                    "  需要下载:" + fileSizeBytes + "  已下载:" + remainingBytes +
                                    " 总大小:" + contentlength + " 已下载总:" + lenth + " 总进度:" + progress);
                            progressListener.onProgress(progress, contentlength);
                            if (lenth == contentlength) {
                                progressListener.onProgress(progress, contentlength, true);
                            }

                        }

                        @Override
                        public void onProgress(Object o, Object o2) {

                        }

                    });
                    AppFramentUtil.tasks.postRunnable(fileDownThred);
                }
            } else {
                progressListener.onError("");
            }
        }
    });
下面在介绍下最底层的  写入文件的一些业务逻辑。

/**
 * 断点多线程下载文件
 *
 * @param url
 * @param downFilePath
 * @param fileName         文件名字带后缀
 * @param progressListener
 */
@Override
public void downFileMultithreadBreakpoint(final long startIndex, final long contentLength,
                                          final String url, final String downFilePath, final String fileName,
                                          final ProgressListener progressListener) {

    Request request = new Request.Builder()
            .addHeader("RANGE", "bytes=" + startIndex + "-" + contentLength)
            .url(url).build();
    String threadId = String.valueOf(Thread.currentThread().getId());
    mFileClient.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            // 下载失败监听回调
            progressListener.onError(e);
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            String dataKey = fileName + startIndex;//多点下载保存本节点的key
            long downSizi = 0;//已经下载的长度
            boolean isOk = false;
            InputStream inputStream = null;
            byte[] buf = new byte[2048];
            int len = 0;
            RandomAccessFile randomAccessFile = null;
            // 储存下载文件的目录
            File dir = new File(downFilePath);

            if (!dir.exists()) {
                dir.mkdirs();
            }
            inputStream = response.body().byteStream();
            long total = response.body().contentLength();
            if (total <= 0) {
                progressListener.onError("");
                return;
            }
            File file = new File(dir, fileName);
            if (!file.exists()) {
                file.createNewFile();
            }
            randomAccessFile = new RandomAccessFile(file, "rw");
            long seekStart = startIndex;
            /**
             * 本地文件已下载的大小
             */
            long them = SharedPreferencesUtil.getSharedPreferencesUtil(mContext).getLong(dataKey);
            AppFramentUtil.logCatUtil.e(TAG, "datakey:" + dataKey + "  downSiz:" + them);

            if (them == contentLength - startIndex) {
                progressListener.onProgress(total, them, dataKey, threadId, isOk);
                return;
            } else {
                seekStart = seekStart + them;
            }

            try {
                randomAccessFile.seek(seekStart);//指定文件写入的位置 。例如上次下载了100个,这次送101开始

                long sum = them;//将本地文件已经下载数量复制给临时变量吗,提供下载进度使用
                inputStream.skip(them);//指定读取流读取的位置,例如上次数据保存了100,这次从101位置开始读取
                while ((len = inputStream.read(buf)) != -1) {
                    randomAccessFile.write(buf, 0, len);
                    sum += len;
                    downSizi = sum;
                    progressListener.onProgress(total, sum, isOk);
                    progressListener.onProgress(total, sum, dataKey, threadId, isOk);
                }
                isOk = true;
                progressListener.onProgress(total, sum, dataKey, threadId, isOk);
                progressListener.onProgress(total, sum, isOk);
                AppFramentUtil.logCatUtil.i(TAG, file.getAbsolutePath());
            } catch (Exception e) {
                e.printStackTrace();
                SharedPreferencesUtil.getSharedPreferencesUtil(mContext).putLong(dataKey, downSizi);
                AppFramentUtil.logCatUtil.e(TAG, "datakey:" + dataKey + "  downSiz:" + downSizi + "/n/r" + e.getStackTrace().toString());
            } finally {
                try {
                    if (inputStream != null)
                        inputStream.close();
                } catch (IOException e) {
                }
                SharedPreferencesUtil.getSharedPreferencesUtil(mContext).putLong(dataKey, downSizi);
                try {
                    if (randomAccessFile != null)
                        randomAccessFile.close();
                } catch (IOException e) {
                }
                if (isOk) {
                    // 下载完成
                    progressListener.onOk(file.getAbsolutePath());
                } else {
                    progressListener.onError("");
                }
            }
        }
    });
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值