多线程断点续传下载实践

来源:互联网 发布:陈奕迅热门歌曲知乎 编辑:程序博客网 时间:2024/06/03 18:45

各位同学,大家好,时隔半年我又回来了。这半年发生了很多事情… 感受颇为深刻,沾染上了赌博,很是后悔 但还能怎么样,接着撸代码吧 17年把输的钱好好敲代码赚回来。 大家如果有什么 项目呀都可以找我做。
有项目可以找我做。
有项目可以找我做。
有项目可以找我做。

卷起袖子开始撸吧 ,大家可以下看下 大概实现的功能以及最终效果;
如果想直接使用 请 进入:https://github.com/LidongWen/DownLoadUtils ;

需要达成的目标

实现的功能

  • 文件可分为多段线程下载
  • 基本功能
    • 开始下载
    • 暂停下载
    • 继续下载
    • 重新下载
  • 自定义
    • 设置 Https 证书、
    • 设置下载地址、
    • 最大下载文件数量等…
  • 数据库操作
    • 获取未完成列表;
    • 获取已完成列表;
    • 自定义获取列表;

最终效果图


不好意思,gif上传失败 示例图请查看:https://github.com/LidongWen/DownLoadUtils/blob/master/img/GIF.gif
或者 下载demo :https://fir.im/downLoad
扫一扫下载

准备知识

看完效果图以后觉得 ok 那么继续往下看,实现以上功能 我们需要掌握哪些知识

知识大纲列表

  • Http原理
  • RandomAccessFile 文件IO处理(文件创建、存放、分段写入)
  • 广播(通知,数据回传,通知客户 我这个下载了 百分之几 了,我下载的速度是多少、我下载失败了 等…)
  • 多线程编程(控制同时运行线程的数量;因为CPU资源有限的,如果CPU负荷过重 会导致我们的APP会挂掉,所以下载线程需要放在线程池中)
  • 数据库(保存我们数据)
  • 服务(可选)

稍微详细一点的 知识列表

  • http原理
    • https 签名认证
    • http头字段
      • http头部参数 http://xiejiaming.com/195.html
      • Range(请求内容字节范围。字节偏移以0开始) 完整格式:
        • Range: bytes=startOffset-targetOffset/sum [表示从startOffset读取,一直读取到targetOffset位置,读取总数为sum直接]
        • Range: bytes=startOffset-targetOffset [字节总数也可以去掉]
        • Range: bytes=startOffset- [从startOffset读取 到文件末尾]
  • RandomAccessFile 随机访问文件流:
    • public void seek(long pos) 设置当前文件指针相对于文件开头的偏移量
  • Service :
    • Service的使用场景:
      • 后台任务,耗时操作 ,且需要在不同的UI内对其控制;
    • Service生命周期;
    • Service启动与销毁;
      • startService()–stopService();
      • bindService()–unbindService();
      • startService()+bindService() – stopService()+unbindService();
    • Service与 UI 通信:
      • 对 UI 操作 影响Service
        • bindService开启服务,UI 获取 binder对象 并通过binger提供的方法 操作 Service;
        • startService() UI 传入数据service ,service通过数据来做相对应的动作;
    • Service 影响 UI
      • 通过广播通知
  • 广播:
    • 广播的生命周期;
    • 广播的使用;
  • 多线程
    • thread 的使用;
    • 线程池 ThreadPoolExecutor 的使用;
  • 数据库
    • sqlLite 升级处理:数据不丢失;
    • sqlLite 增删查改的基本使用;

思路

  • 使用 http 的 Range 字段 控制 拉取具体数据(比如 文件A 有10000字节,我可以控制 我只需要 500-1000 这段数据)
  • RandomAccessFile 分段写入(比如文件 10000字节, 我可以 0-500 ,500-1000 写入)
  • 线程池(控制最大下载的数量,下载队列…)
  • 广播(通知)
  • 数据库(保存下载的状态、进度 等待信息)
  • 以上知识通过逻辑相互调用,就能做出一个多线程断点续传下载功能啦

我就写一下核心代码;

http 下载部分、写入文件流部分

将一个文件分为若干部分,分别下载,在下载过程中发送进度通知 UI。
我们先获取文件的长度,分为若干各部分分段下载

这边我一个文件分为3部分下载,得到三个 ThreadInfo(包含了每一段线程的信息,开始字节点、结束字节点、下载url):

int length = mFileInfo.getLength() / 3; // 获取单个线程下载长度for (int i = 0; i < mThreadNum; i++) {            // 创建线程信息    ThreadInfo threadInfo = new ThreadInfo();    threadInfo.setId(mFileInfo.getId() + "_" + i);    threadInfo.setUrl(mFileInfo.getUrl());    threadInfo.setStart(length * i);  //开始节点    threadInfo.setEnd(((i + 1) * length - 1));  //结束节点    threadInfo.setFinished(0);    threadInfo.setFileId(mFileInfo.getId());    threadInfo.setMd5(mFileInfo.getMd5());    threadInfo.setOver(false);    threadInfo.setOvertime("none");    if (i == mThreadNum - 1) {        threadInfo.setEnd(mFileInfo.getLength());// 设置最后一个线程的下载长度    }    threadInfoList.add(threadInfo);    // 添加线程信息到集合}

线程池启动三个线程分别下载ThreadInfo
下载的部分(这边大概写下 http数据流获取, 写入文件):

  HttpURLConnection conn = null;  conn = HttpUtils.getInstance().createConnection(mThreadInfo.getUrl());  conn.setRequestProperty("Range", "bytes=" + start + "-" + mThreadInfo.getEnd()); // 指定字节段  0~300..  if (conn.getResponseCode() == 206) {//注意这边 返回码是 206  input =conn.getInputStream();//得到输入流  //写入文件  File file = new File(DownloadConfig.getFileDir(), mFileInfo.getFileName());  raf = new RandomAccessFile(file, "rwd");  raf.seek(start);//指定位置开始写入    while ((len = input.read(buf)) != -1) {   //写入文件   raf.write(buf, 0, len);   mFinishedLen += len;//    /**    *  通知UI更新进度条    */    ....  }  ....

线程池

这部分 我直接使用 Executors.newFixedThreadPool

 public static ExecutorService sExecutorService = Executors.newFixedThreadPool(5); //同时运行的线程数量,超过5个时,其他线程进入等待状态。 sExecutorService.execute(downloadThread);//加入线程池

数据库

作用:管理保存各个文件的状态 下载进度等信息。
这边我用的 greenDao,增删查改 等… 具体不写了

广播

在下在过程中 发送广播通知UI更新下载进度

Intent intent = new Intent(IntentAction.ACTION_UPDATE);intent.putExtra(KeyName.FINISHED_TAG, mFinishedLen);....intent.putExtra(KeyName.OTHER_MESSAGE, ((Serializable) postion));mContext.sendBroadcast(intent);

以上是发送更新广播, 暂停、结束等广播发送与其类似。就看你何时调用 传啥数据。

核心技术实现部分就几乎是以上部分了。
具体逻辑请查看代码具体代码清查看https://github.com/LidongWen/DownLoadUtils/tree/master/DownLoader;
基本上了解完上面的知识,代码就非常容易理解了, 没有理解又着急用也没有关系,我已经将它做成框架,几行代码就能集成使用啦

具体用法 API 请 进入:https://github.com/LidongWen/DownLoadUtils ;

0 0
原创粉丝点击