转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/120956134
本文出自【赵彦军的博客】

文章目录

  • 下载原理
  • 断点续传原理
    • 1、java.io.RandomAccessFile
    • 2、请求响应码及Header
    • 3、处理请求资源发生改变的问题
    • 4、文件可复用判断

下载原理

在介绍断点续传之前,我们先说说下载的原理。代码示例用 OkHttp 作为示例。

下载核心思路是把 responseBody 写入文件,核心代码如下:

但是这种做法有个明显的问题,假如手机在下载文件的时候下载了80%,某些原因断网了,如果不支持断点续传,那就只有被迫重头开始下载。但是如果有断点续传的加持,就只需要下载最后 20% 的资源,避免重新下载。

断点续传原理

1、java.io.RandomAccessFile

断点续传/下载需要使用到 java.io.RandomAccessFile 类,RandomAccessFile 的实例支持读取和写入随机访问文件,它也可以 seek(long pos) 设置从此文件的开头开始测量的文件指针偏移量,在该位置进行下一次读取或写入操作。简单点说就是可以通过 seek(long pos)方法跳过pos字节开始写入字节。

如何创建一个 RandomAccessFile ?

//rw:支持可读可写
val file = RandomAccessFile(file, "rw")

假如我有一段文本要写入文件,但是需要从文件的第100个字节开始写,那么需要调用 seek(long pos ) 方法跳过前 100 个字节, 具体实现如下:

val file = RandomAccessFile(file, "rw")
file.seek(100)
file.writeBytes(.....)

所以,我们可以把 responseBody 写入文件的实现改成如下实现:

2、请求响应码及Header

正常情况下,我们下载一个文件,响应码是 200

responseBody 返回的是整个文件的内容。如何才能返回部分内容?
在实现分块请求之前,首先要判断服务器是否支持分块返回,标志是 Access-Ranges ,值为 bytes 就代表服务器支持分块返回,也就是支持断点续传。

所以,我们可以封装一个方法,来判断服务是否支持断点续传

在确定了服务器支持分块传输后,我们就可以在请求资源的时候,添加 header 请求 Range 字段,来指定请求实体的范围。它的范围取值是在 0-Content-Length 之间,使用 - 分割。

例如已经下载了 1000 bytes 的资源内容,想接着继续下载之后的资源内容,只要在 HTTP 请求头部,增加 Range:bytes=1000- 就可以了。

Range 还有几种不同的方式来限定范围,可以根据需要灵活定制:

  • 500-1000:指定开始和结束的范围。
  • 500- :指定开始区间,一直传递到结束。这个就比较适用于断点续传、或者在线播放等等。
  • -500:无开始区间,只意思是需要最后 500 bytes 的内容实体。
  • 100-300,1000-3000:指定多个范围,这种方式使用的场景很少,了解一下就好了。

具体举例如下:

通过抓包我们发现,添加了 Range 后,请求响应码变成了 206


并且添加 Range 后,响应的 responseBody 就不再是整个文件的内容了,而是一个片段,我们只需要把这个片段的数据直接写入文件就可以。

注意事项,如何计算 Range 请求头的开始值,一般取用缓存文件的长度值,如下

val start = targetFile.length()

3、处理请求资源发生改变的问题

在现实的场景中,服务器中的文件是会有发生变化的情况的,那么我们发起续传的请求肯定是失败的

那么为了处理这种服务器文件资源发生改变的问题,在 RFC2616 中定义了 Last-ModifiedEtag 来判断续传文件资源是否发生改变。

  • Last-Modified:记录 Http 页面最后修改时间的 Http 头部参数,Last-Modified 是由服务端发送给客户端的
  • Etag:作为文件的唯一标志,这个标志可以是文件的 hash 值或者是一个版本

我们封装一个方法用来获取这两个值

if-Range:用于判断实体是否发生改变,如果实体未改变,服务器发送客户端丢失的部分,否则发送整个实体。一般格式:

If-Range: Etag | HTTP-Date

If-Range 可以使用 Etag 或者 Last-Modified 返回的值。当没有 ETage 却有 Last-modified 时,可以把 Last-modified 作为 If-Range 字段的值。实现举例如下:

关于 Etag 、Last-Modified 注意事项:

  • 当第一次请求资源的时候,获取 Etag/Last-Modified 值,并且保存到本地
  • 当下一次需要断点续传的时候,把 Etag/Last-Modified 值取出,添加到请求头 If-Range 字段

4、文件可复用判断

在文件下载完成后,如果又发起了一次下载请求,那么此时不应该重复下载的。需要做可复用判断,思路如下

  • 1、获取目前已下载文件的大小
  • 2、获取文件url 获取服务器文件大小
  • 3、如果已下载大小等于服务器文件大小,则复用已下载的文件

举例如下:

//获取已下载文件长度
val downloadLength = targetFile.length()
//获取服务器文件长度
val contentLength = responseBody.contentLength()
//两者长度对比
if(downloadLength == contentLength){//复用已下载文件,不再重复下载
}

Android 断点续传实现原理相关推荐

  1. 断点续传的原理剖析与实例讲解

    断点续传的原理剖析与实例讲解 本文所要讲的是Android断点续传的内容,以实例的形式进行了详细介绍. 一.断点续传的原理 其实断点续传的原理很简单,就是在http的请求上和一般的下载有所不同而已. ...

  2. Android Touch事件原理加实例分析

    Android中有各种各样的事件,以响应用户的操作.这些事件可以分为按键事件和触屏事件.而Touch事件是触屏事件的基础事件,在进行Android开发时经常会用到,所以非常有必要深入理解它的原理机制. ...

  3. Android 插件化原理学习 —— Hook 机制之动态代理

    前言 为了实现 App 的快速迭代更新,基于 H5 Hybrid 的解决方案有很多,由于 webview 本身的性能问题,也随之出现了很多基于 JS 引擎实现的原生渲染的方案,例如 React Nat ...

  4. 【Android 安全】DEX 加密 ( Application 替换 | Android 应用启动原理 | ActivityThread 后续分析 | Application 替换位置 )

    文章目录 一.ActivityThread 后续分析 二.ActivityThread 相关源码 三.Application 替换位置 dex 解密时 , 需要将 代理 Application 替换为 ...

  5. 手机内存解读以及android刷机原理

    手机内存分为:机身内存.RAM.ROM 机身内存:相当于 电脑的硬盘 这个一般可以外加SD卡扩充: RAM:相当于 电脑的内存条 可读写,掉电清空 ROM:相当于 电脑的 BIOS ROM 原则上只读 ...

  6. Android 插件化原理解析——Activity生命周期管理

    之前的 Android插件化原理解析 系列文章揭开了Hook机制的神秘面纱,现在我们手握倚天屠龙,那么如何通过这种技术完成插件化方案呢?具体来说,插件中的Activity,Service等组件如何在A ...

  7. Android代码入侵原理解析(一)

    Original 2017-05-06 付超红 滴滴安全应急响应中心 2017年初,在滴滴安全沙龙上,滴滴出行安全专家--付超红,针对App的攻与防进行了分享.会后大家对这个议题反响热烈,纷纷求详情求 ...

  8. Android 插件化原理解析——Hook机制之AMSPMS

    在前面的文章中我们介绍了DroidPlugin的Hook机制,也就是代理方式和Binder Hook:插件框架通过AOP实现了插件使用和开发的透明性.在讲述DroidPlugin如何实现四大组件的插件 ...

  9. android r.java 原理,深入理解Android消息处理系统原理

    Android应用程序也是消息驱动的,按道理来说也应该提供消息循环机制.实际上谷歌参考了Windows的消息循环机制,也在Android系统中实现了消息循环机制. Android通过Looper.Ha ...

最新文章

  1. 中object转为list集合_java基础集合小结
  2. 使用 NuGet 管理我们的程序集 - 预发行版
  3. 一个虐你千百遍的问题:“RPC好,还是RESTful好?”
  4. 使用jqprint插件完成页面打印
  5. 【渝粤题库】国家开放大学2021春1708农业产业发展规划题目
  6. Window10 安装虚拟机搭建LInux系统
  7. 安卓入门程序《发短信》
  8. 可以“作为医生”的 GPT-3,究竟是炒作还是名副其实?
  9. 修改fstab导致linux无法启动的解决
  10. 怎么查看ubuntu虚拟机ip地址?
  11. 爬取淘宝买家秀,sign值的生成
  12. littleVGL开发(10):事件(EVENT)
  13. mysql进阶(十九)SQL语句如何精准查找某一时间段的数据
  14. 使用蒙特卡罗方法计算圆周率
  15. 如何利用番茄工作法提高学习和工作的效率
  16. 【黄啊码】vue-pdf预览时无法显示印章和中文字体或者乱码(简单粗暴)
  17. Qt创建线程两种方式的区别
  18. 量子力学第二弹——基本假设
  19. CSDN积分获取方法(转载)
  20. 详解SEO搜索引擎优化

热门文章

  1. python函数体以冒号起始、并且是缩进格式的_Python基础第三课
  2. python将object转换为float_将pandas系列类型从object转换为float64
  3. oracle12c的scott,Oracle12C创建scott账户
  4. php 爬虫 类,php爬虫原型
  5. plotly使用mapbox实现地图可视化
  6. python的shell无法输入_python中shell如何逐行输入?
  7. 邮件发送代码--网易服务器代理
  8. 机器翻译中丢掉词向量层会怎样?
  9. 直播 | SemEval-2020自由文本关系抽取冠军方案解读(附NLP竞赛常用技巧总结)
  10. 多目标跟踪(MOT)领域近期值得读的几篇论文