热门标签 | HotTags
当前位置:  开发笔记 > 数据库 > 正文

Android断点续传原理以及实现

这篇文章主要介绍了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 get(String url); 
  //是否存在 
  public boolean isExits(String url); 
} 

5.  具体的文件下载逻辑

public class DownLoadUtil { 
  //构造方法略 
  public void download(){ 
    List 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.  效果如下所示


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


推荐阅读
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • QUIC协议:快速UDP互联网连接
    QUIC(Quick UDP Internet Connections)是谷歌开发的一种旨在提高网络性能和安全性的传输层协议。它基于UDP,并结合了TLS级别的安全性,提供了更高效、更可靠的互联网通信方式。 ... [详细]
  • Windows服务与数据库交互问题解析
    本文探讨了在Windows 10(64位)环境下开发的Windows服务,旨在定期向本地MS SQL Server (v.11)插入记录。尽管服务已成功安装并运行,但记录并未正确插入。我们将详细分析可能的原因及解决方案。 ... [详细]
  • SQL中UPDATE SET FROM语句的使用方法及应用场景
    本文详细介绍了SQL中UPDATE SET FROM语句的使用方法,通过具体示例展示了如何利用该语句高效地更新多表关联数据。适合数据库管理员和开发人员参考。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 本文介绍了如何在具备多个IP地址的FTP服务器环境中,通过动态地址端口复用和地址转换技术优化网络配置。重点讨论了2Mb/s DDN专线连接、Cisco 2611路由器及内部网络地址规划。 ... [详细]
  • 深入理解 SQL 视图、存储过程与事务
    本文详细介绍了SQL中的视图、存储过程和事务的概念及应用。视图为用户提供了一种灵活的数据查询方式,存储过程则封装了复杂的SQL逻辑,而事务确保了数据库操作的完整性和一致性。 ... [详细]
  • 构建基于BERT的中文NL2SQL模型:一个简明的基准
    本文探讨了将自然语言转换为SQL语句(NL2SQL)的任务,这是人工智能领域中一项非常实用的研究方向。文章介绍了笔者在公司举办的首届中文NL2SQL挑战赛中的实践,该比赛提供了金融和通用领域的表格数据,并标注了对应的自然语言与SQL语句对,旨在训练准确的NL2SQL模型。 ... [详细]
  • 数据库内核开发入门 | 搭建研发环境的初步指南
    本课程将带你从零开始,逐步掌握数据库内核开发的基础知识和实践技能,重点介绍如何搭建OceanBase的开发环境。 ... [详细]
  • 本文深入探讨 MyBatis 中动态 SQL 的使用方法,包括 if/where、trim 自定义字符串截取规则、choose 分支选择、封装查询和修改条件的 where/set 标签、批量处理的 foreach 标签以及内置参数和 bind 的用法。 ... [详细]
  • 如何配置Unturned服务器及其消息设置
    本文详细介绍了Unturned服务器的配置方法和消息设置技巧,帮助用户了解并优化服务器管理。同时,提供了关于云服务资源操作记录、远程登录设置以及文件传输的相关补充信息。 ... [详细]
  • 如何在WPS Office for Mac中调整Word文档的文字排列方向
    本文将详细介绍如何使用最新版WPS Office for Mac调整Word文档中的文字排列方向。通过这些步骤,用户可以轻松更改文本的水平或垂直排列方式,以满足不同的排版需求。 ... [详细]
  • 本文总结了在使用Ionic 5进行Android平台APK打包时遇到的问题,特别是针对QRScanner插件的改造。通过详细分析和提供具体的解决方法,帮助开发者顺利打包并优化应用性能。 ... [详细]
  • 网络攻防实战:从HTTP到HTTPS的演变
    本文通过一系列日记记录了从发现漏洞到逐步加强安全措施的过程,探讨了如何应对网络攻击并最终实现全面的安全防护。 ... [详细]
  • UNP 第9章:主机名与地址转换
    本章探讨了用于在主机名和数值地址之间进行转换的函数,如gethostbyname和gethostbyaddr。此外,还介绍了getservbyname和getservbyport函数,用于在服务器名和端口号之间进行转换。 ... [详细]
author-avatar
踢出个未来为我鼓掌
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有