Android 断点续传原理以及实现
发布时间 - 2026-01-10 21:57:16 点击率:次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快速实现断点续传的方法
# 自定义
# 断点续传
# 会在
# 讲到
# 将该
# 都是
# 判断是否
# 有一
# 是在
# 本地下载
# 就不
# 也在
# 而在
# 并在
# 从哪里
# 希望能
# 到哪里
# 所示
# 谢谢大家
# 后会
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Laravel如何实现RSS订阅源功能_Laravel动态生成网站XML格式订阅内容【教程】
Android仿QQ列表左滑删除操作
为什么php本地部署后css不生效_静态资源加载失败修复技巧【技巧】
Laravel如何生成和使用数据填充?(Seeder和Factory示例)
如何在宝塔面板中创建新站点?
浅谈Javascript中的Label语句
Laravel怎么实现模型属性转换Casting_Laravel自动将JSON字段转为数组【技巧】
5种Android数据存储方式汇总
网站制作公司哪里好做,成都网站制作公司哪家做得比较好,更正规?
谷歌浏览器下载文件时中断怎么办 Google Chrome下载管理修复
中山网站推广排名,中山信息港登录入口?
html5如何实现懒加载图片_ intersectionobserver api用法【教程】
消息称 OpenAI 正研发的神秘硬件设备或为智能笔,富士康代工
Laravel怎么多语言本地化设置_Laravel语言包翻译与Locale动态切换【手册】
WEB开发之注册页面验证码倒计时代码的实现
UC浏览器如何切换小说阅读源_UC浏览器阅读源切换【方法】
Bootstrap整体框架之CSS12栅格系统
Android实现代码画虚线边框背景效果
CSS3怎么给轮播图加过渡动画_transition加transform实现【技巧】
Laravel Admin后台管理框架推荐_Laravel快速开发后台工具
利用python获取某年中每个月的第一天和最后一天
如何在宝塔面板创建新站点?
Python高阶函数应用_函数作为参数说明【指导】
网站广告牌制作方法,街上的广告牌,横幅,用PS还是其他软件做的?
高防服务器租用首荐平台,企业级优惠套餐快速部署
宙斯浏览器怎么屏蔽图片浏览 节省手机流量使用设置方法
lovemo网页版地址 lovemo官网手机登录
网站视频制作书签怎么做,ie浏览器怎么将网站固定在书签工具栏?
Laravel如何处理和验证JSON类型的数据库字段
Win11应用商店下载慢怎么办 Win11更改DNS提速下载【修复】
如何在建站宝盒中设置产品搜索功能?
Laravel安装步骤详细教程_Laravel环境搭建指南
Laravel队列任务超时怎么办_Laravel Queue Timeout设置详解
浅述节点的创建及常见功能的实现
php在windows下怎么调试_phpwindows环境调试操作说明【操作】
Laravel如何使用缓存系统提升性能_Laravel缓存驱动和应用优化方案
Laravel项目结构怎么组织_大型Laravel应用的最佳目录结构实践
HTML 中动态设置元素 name 属性的正确语法详解
laravel怎么配置Redis作为缓存驱动_laravel Redis缓存配置教程
Claude怎样写结构化提示词_Claude结构化提示词写法【教程】
图片制作网站免费软件,有没有免费的网站或软件可以将图片批量转为A4大小的pdf?
Edge浏览器提示“由你的组织管理”怎么解决_去除浏览器托管提示【修复】
DeepSeek是免费使用的吗 DeepSeek收费模式与Pro版本功能详解
Laravel如何使用Guzzle调用外部接口_Laravel发起HTTP请求与JSON数据解析【详解】
JavaScript模板引擎Template.js使用详解
Microsoft Edge如何解决网页加载问题 Edge浏览器加载问题修复
阿里云高弹*务器配置方案|支持分布式架构与多节点部署
魔毅自助建站系统:模板定制与SEO优化一键生成指南
悟空识字怎么关闭自动续费_悟空识字取消会员自动扣费步骤
香港服务器网站测试全流程:性能评估、SEO加载与移动适配优化

