全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

Android 断点续传原理以及实现

Android 断点续传原理以及实现

0.  前言

在Android开发中,断点续传听起来挺容易,在下载一个文件时点击暂停任务暂停,点击开始会继续下载文件。但是真正实现起来知识点还是蛮多的,因此今天有时间实现了一下,并进行记录。

1.  断点续传原理

在本地下载过程中要使用数据库实时存储到底存储到文件的哪个位置了,这样点击开始继续传递时,才能通过HTTP的GET请求中的setRequestProperty()方法可以告诉服务器,数据从哪里开始,到哪里结束。同时在本地的文件写入时,RandomAccessFile的seek()方法也支持在文件中的任意位置进行写入操作。同时通过广播将子线程的进度告诉Activity的ProcessBar。

2.  Activity的按钮响应

当点击开始按钮时,将url写在了FileInfo类的对象info中并通过Intent从Activity传递到了Service中。这里使用setAction()来区分是开始按钮还是暂停按钮。

public class FileInfo implements Serializable{ 
  private String url; //URL 
  private int length; //长度或结束位置 
  private int start; //开始位置 
  private int now;//当前进度 
//构造方法,set/get略 
} 
//开始按钮逻辑,停止逻辑大致相同 
strat.setOnClickListener(new View.OnClickListener() { 
   @Override 
   public void onClick(View view) { 
    Intent intent = new Intent(MainActivity.this,DownLoadService.class); 
    intent.setAction(DownLoadService.ACTION_START); 
    intent.putExtra("fileUrl",info); 
    startService(intent); 
  } 
}); 

3.  在Service中的子线程中获取文件大小

在Service中的onStartCommand()中,将FileInfo对象从Intent中取出,如果是开始命令,则开启一个线程,根据该url去获得要下载文件的大小,将该大小写入对象并通过Handler传回Service,同时在本地创建一个相同大小的本地文件。暂停命令最后会讲到。

public void run() { 
      HttpURLConnection urlConnection = null; 
      RandomAccessFile randomFile = null; 
      try { 
        URL url = new URL(fileInfo.getUrl()); 
        urlConnection = (HttpURLConnection) url.openConnection(); 
        urlConnection.setConnectTimeout(3000); 
        urlConnection.setRequestMethod("GET"); 
        int length = -1; 
        if (urlConnection.getResponseCode() == HttpStatus.SC_OK) { 
          //获得文件长度 
          length = urlConnection.getContentLength(); 
        } 
        if (length <= 0) { 
          return; 
        } 
        //创建相同大小的本地文件 
        File dir = new File(DOWNLOAD_PATH); 
        if (!dir.exists()) { 
          dir.mkdir(); 
        } 
        File file = new File(dir, FILE_NAME); 
        randomFile = new RandomAccessFile(file, "rwd"); 
        randomFile.setLength(length); 
        //长度给fileInfo对象 
        fileInfo.setLength(length); 
        //通过Handler将对象传递给Service 
        mHandle.obtainMessage(0, fileInfo).sendToTarget(); 
      } catch (Exception e) { 
        e.printStackTrace(); 
      } finally { //流的回收逻辑略 
      } 
    } 
  } 

4.  数据库操作封装

在Service的handleMessage()方法中拿到有length属性的FileInfo对象,并使用自定义的DownLoadUtil类进行具体的文件下载逻辑。这里传入上下文,因为数据库处理操作需要用到。

downLoadUtil = new DownLoadUtil(DownLoadService.this,info);

downLoadUtil.download();

这里有一个数据库操作的接口ThreadDAO,内部有增删改查等逻辑,用于记录下载任务的信息。自定义一个ThreadDAOImpl类将这里的逻辑实现,内部数据库创建关于继承SQLiteOpenHelper的自定义类的逻辑就不贴了,比较简单,该类会在ThreadDAOImpl类的构造方法中创建实例。完成底层数据库操作的封装。

public interface ThreadDAO { 
  //插入一条数据 
  public void insert(FileInfo info); 
  //根据URL删除一条数据 
  public void delete(String url); 
  //根据URL更新一条进度 
  public void update(String url,int finished); 
  //根据URL找到一条数据 
  public List<FileInfo> get(String url); 
  //是否存在 
  public boolean isExits(String url); 
} 

5.  具体的文件下载逻辑

public class DownLoadUtil { 
  //构造方法略 
  public void download(){ 
    List<FileInfo> lists = threadDAO.get(fileInfo.getUrl()); 
    FileInfo info = null; 
    if(lists.size() == 0){ 
      //第一次下载,创建子线程下载 
      new MyThread(fileInfo).start(); 
    }else{ 
      //中间开始的 
      info = lists.get(0); 
      new MyThread(info).start(); 
    } 
  } 
 
  class MyThread extends Thread{ 
    private FileInfo info = null; 
    public MyThread(FileInfo threadInfo) { 
      this.info = threadInfo; 
    } 
    @Override 
    public void run() { 
      //向数据库添加线程信息 
      if(!threadDAO.isExits(info.getUrl())){ 
        threadDAO.insert(info); 
      } 
      HttpURLConnection urlConnection = null; 
      RandomAccessFile randomFile =null; 
      InputStream inputStream = null; 
      try { 
        URL url = new URL(info.getUrl()); 
        urlConnection = (HttpURLConnection) url.openConnection(); 
        urlConnection.setConnectTimeout(3000); 
        urlConnection.setRequestMethod("GET"); 
        //设置下载位置 
        int start = info.getStart() + info.getNow(); 
        urlConnection.setRequestProperty("Range","bytes=" + start + "-" + info.getLength()); 
 
        //设置文件写入位置 
        File file = new File(DOWNLOAD_PATH,FILE_NAME); 
        randomFile = new RandomAccessFile(file, "rwd"); 
        randomFile.seek(start); 
 
        //向Activity发广播 
        Intent intent = new Intent(ACTION_UPDATE); 
        finished += info.getNow(); 
 
        if (urlConnection.getResponseCode() == HttpStatus.SC_PARTIAL_CONTENT) { 
          //获得文件流 
          inputStream = urlConnection.getInputStream(); 
          byte[] buffer = new byte[512]; 
          int len = -1; 
          long time = System.currentTimeMillis(); 
          while ((len = inputStream.read(buffer))!= -1){ 
            //写入文件 
            randomFile.write(buffer,0,len); 
            //把进度发送给Activity 
            finished += len; 
            //看时间间隔,时间间隔大于500ms再发 
            if(System.currentTimeMillis() - time >500){ 
              time = System.currentTimeMillis(); 
              intent.putExtra("now",finished *100 /fileInfo.getLength()); 
              context.sendBroadcast(intent); 
            } 
            //判断是否是暂停状态 
            if(isPause){ 
              threadDAO.update(info.getUrl(),finished); 
              return; //结束循环 
            } 
          } 
          //删除线程信息 
          threadDAO.delete(info.getUrl()); 
        } 
      }catch (Exception e){ 
        e.printStackTrace(); 
      }finally {//回收工作略 
      } 
    } 
  } 
} 

上面也讲到使用自定义的DownLoadUtil类进行具体的文件下载逻辑,这也是最关键的部分了,在该类的构造方法中进行ThreadDAOImpl实例的创建。并在download()中通过数据库查询的操作,判断是否是第一次开始下载任务,如果是,则开启一个子线程MyThread进行下载任务,否则将进度信息从数据库中取出,并将该信息传递给MyThread。

在MyThread中,通过info.getStart() + info.getNow()设置开始下载的位置,如果是第一次下载两个数将都是0,如果是暂停后再下载,则info.getNow()会取出非0值,该值来自数据库存储。使用setRequestProperty告知服务器从哪里开始传递数据,传递到哪里结束,本地使用RandomAccessFile的seek()方法进行数据的本地存储。使用广播将进度的百分比传递给Activity,Activity再改变ProcessBar进行UI调整。

这里很关键的一点是在用户点击暂停后会在Service中调用downLoadUtil.isPause = true,因此上面while循环会结束,停止下载并通过数据库的update()保存进度值。从而在续传时取出该值,重新对服务器发起文件起始点的下载任务请求,同时也在本地文件的相应位置继续写入操作。

6.  效果如下所示


感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!


# Android  # 断点续传原理  # 断点续传原理详细介绍  # 断点续传原理详解和实例  # 详解Android使用OKHttp3实现下载(断点续传、显示进度)  # android实现多线程下载文件(支持暂停、取消、断点续传)  # android使用OkHttp实现下载的进度监听和断点续传  # Android多线程断点续传下载功能实现代码  # Android多线程+单线程+断点续传+进度条显示下载功能  # Android 断点续传的原理剖析与实例讲解  # Android实现网络多线程断点续传下载实例  # Android编程开发实现多线程断点续传下载器实例  # Android快速实现断点续传的方法  # 自定义  # 断点续传  # 会在  # 讲到  # 将该  # 都是  # 判断是否  # 有一  # 是在  # 本地下载  # 就不  # 也在  # 而在  # 并在  # 从哪里  # 希望能  # 到哪里  # 所示  # 谢谢大家  # 后会 


相关文章: 如何快速登录WAP自助建站平台?  完全自定义免费建站平台:主题模板在线生成一站式服务  微信小程序 input输入框控件详解及实例(多种示例)  建站之星如何优化SEO以实现高效排名?  ,想在网上投简历,哪几个网站比较好?  如何在景安云服务器上绑定域名并配置虚拟主机?  宝华建站服务条款解析:五站合一功能与SEO优化设置指南  专业商城网站制作公司有哪些,pi商城官网是哪个?  如何挑选高效建站主机与优质域名?  实现虚拟支付需哪些建站技术支撑?  linux top下的 minerd 木马清除方法  如何彻底删除建站之星生成的Banner?  高防服务器:AI智能防御DDoS攻击与数据安全保障  如何在橙子建站中快速调整背景颜色?  建站之星代理商如何保障技术支持与售后服务?  建站之星如何开启自定义404页面避免用户流失?  小型网站建站如何选择虚拟主机?  ,石家庄四十八中学官网?  详解jQuery停止动画——stop()方法的使用  定制建站流程解析:需求评估与SEO优化功能开发指南  网页设计网站制作软件,microsoft office哪个可以创建网页?  表情包在线制作网站免费,表情包怎么弄?  阿里云网站制作公司,阿里云快速搭建网站好用吗?  ,交易猫的商品怎么发布到网站上去?  建站主机如何选?高性价比方案全解析  音乐网站服务器如何优化API响应速度?  天津个人网站制作公司,天津网约车驾驶员从业资格证官网?  韩国代理服务器如何选?解析IP设置技巧与跨境访问优化指南  如何高效生成建站之星成品网站源码?  广州网站制作的公司,现在专门做网站的公司有没有哪几家是比较好的,性价比高,模板也多的?  小建面朝正北,A点实际方位是否存在偏差?  头像制作网站在线观看,除了站酷,还有哪些比较好的设计网站?  招贴海报怎么做,什么是海报招贴?  b2c电商网站制作流程,b2c水平综合的电商平台?  赚钱网站制作软件,建一个网站怎样才能赚钱?是如何盈利的?  标准网站视频模板制作软件,现在有哪个网站的视频编辑素材最齐全的,背景音乐、音效等?  建站主机与服务器功能差异如何区分?  网站专业制作公司有哪些,做一个公司网站要多少钱?  深圳企业网站制作设计,在深圳如何网上全流程注册公司?  如何访问已购建站主机并解决登录问题?  已有域名能否直接搭建网站?  家庭建站与云服务器建站,如何选择更优?  如何选择最佳自助建站系统?快速指南解析优劣  宁波免费建站如何选择可靠模板与平台?  如何通过虚拟主机快速完成网站搭建?  湖州网站制作公司有哪些,浙江中蓝新能源公司官网?  清除minerd进程的简单方法  如何高效搭建专业期货交易平台网站?  我的世界制作壁纸网站下载,手机怎么换我的世界壁纸?  如何快速启动建站代理加盟业务? 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。