有趣的比喻故事
想象一下,小明正在抄写一本很厚的书《Android开发秘籍》。这本书有1000页,他不可能一次性抄完。
故事场景:
-
普通下载:就像小明每次都是从第1页开始抄,即使昨天已经抄到第500页了
-
断点续传:聪明的小明会:
- 在书签上记录"已抄到第500页"
- 下次继续从第501页开始抄
- 即使中途被打断,也不会白费功夫
技术原理解析
核心概念
java
// 就像小明的读书笔记
public class DownloadInfo {
private String fileUrl; // 书的地址
private String savePath; // 笔记本位置
private long totalSize; // 书的总页数:1000页
private long downloadedSize; // 已抄页数:500页
private boolean isPaused; // 是否暂停
}
HTTP协议支持
服务器就像图书管理员,支持"从指定位置阅读":
java
// 告诉管理员:"请从第501页开始给我书的内容"
connection.setRequestProperty("Range", "bytes=" + downloadedSize + "-");
完整代码实现
1. 下载信息管理(小明的书签)
java
public class DownloadRecord {
private String url;
private String filePath;
private long totalSize;
private long currentSize;
// 保存读书进度
public void saveProgress() {
SharedPreferences sp = context.getSharedPreferences("download_progress", Context.MODE_PRIVATE);
sp.edit().putLong(url + "_current", currentSize)
.putLong(url + "_total", totalSize)
.apply();
}
// 读取读书进度
public void loadProgress() {
SharedPreferences sp = context.getSharedPreferences("download_progress", Context.MODE_PRIVATE);
currentSize = sp.getLong(url + "_current", 0);
totalSize = sp.getLong(url + "_total", 0);
}
}
2. 智能下载器(聪明的小明)
java
public class SmartDownloader {
private DownloadRecord record;
private boolean isPaused = false;
public void startDownload(String url, String savePath) {
new Thread(() -> {
try {
HttpURLConnection connection = createConnection(url);
// 关键步骤1:检查这本书能不能跳着读
boolean supportBreakpoint = checkBreakpointSupport(connection);
// 关键步骤2:设置从哪开始读
if (supportBreakpoint && record.getCurrentSize() > 0) {
connection.setRequestProperty("Range", "bytes=" + record.getCurrentSize() + "-");
}
// 关键步骤3:开始读书并记录进度
downloadContent(connection, savePath);
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
private void downloadContent(HttpURLConnection connection, String savePath) {
try (InputStream input = connection.getInputStream();
RandomAccessFile output = new RandomAccessFile(savePath, "rw")) {
// 跳到上次写到的位置继续写
output.seek(record.getCurrentSize());
byte[] buffer = new byte[8192]; // 每次读8KB
int bytesRead;
while ((bytesRead = input.read(buffer)) != -1 && !isPaused) {
// 写入笔记本
output.write(buffer, 0, bytesRead);
// 更新进度
record.setCurrentSize(record.getCurrentSize() + bytesRead);
record.saveProgress(); // 及时保存书签
// 通知UI更新进度条
updateProgressUI(record.getCurrentSize(), record.getTotalSize());
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void pause() {
isPaused = true;
record.saveProgress(); // 暂停时一定要保存书签
}
public void resume() {
isPaused = false;
startDownload(record.getUrl(), record.getFilePath());
}
}
3. 服务器能力检测(询问图书管理员)
java
private boolean checkBreakpointSupport(HttpURLConnection connection) {
try {
// 方法1:检查Accept-Ranges头
String acceptRanges = connection.getHeaderField("Accept-Ranges");
if ("bytes".equals(acceptRanges)) {
return true;
}
// 方法2:检查Content-Range头(更可靠)
String contentRange = connection.getHeaderField("Content-Range");
if (contentRange != null && contentRange.startsWith("bytes")) {
return true;
}
// 方法3:直接尝试Range请求
connection.setRequestProperty("Range", "bytes=0-0");
if (connection.getResponseCode() == 206) { // 206表示部分内容
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
时序图:整个调用过程

高级特性:多线程断点续传
就像小明请了3个朋友一起抄书:
java
public class MultiThreadDownloader {
private static final int THREAD_COUNT = 3;
public void multiThreadDownload(String url, String savePath) {
// 1. 获取文件总大小
long fileSize = getFileSize(url);
long blockSize = fileSize / THREAD_COUNT;
// 2. 为每个朋友分配任务
for (int i = 0; i < THREAD_COUNT; i++) {
long startPos = i * blockSize;
long endPos = (i == THREAD_COUNT - 1) ? fileSize - 1 : (i + 1) * blockSize - 1;
new DownloadThread(i, url, savePath, startPos, endPos).start();
}
}
class DownloadThread extends Thread {
private int threadId;
private String url;
private String savePath;
private long startPos;
private long endPos;
@Override
public void run() {
// 每个线程独立记录自己的进度
downloadBlock(threadId, url, savePath, startPos, endPos);
}
}
}
实际应用技巧
1. 进度保存策略
java
// 不要每次写入都保存,太频繁影响性能
private void smartSaveProgress() {
long currentTime = System.currentTimeMillis();
if (currentTime - lastSaveTime > 1000) { // 每秒保存一次
record.saveProgress();
lastSaveTime = currentTime;
}
}
2. 异常处理
java
private void downloadWithRetry() {
int retryCount = 0;
while (retryCount < MAX_RETRY) {
try {
downloadContent();
break;
} catch (IOException e) {
retryCount++;
if (retryCount == MAX_RETRY) {
// 通知用户网络异常
notifyNetworkError();
} else {
// 等待后重试
SystemClock.sleep(2000);
}
}
}
}
总结
断点续传就像聪明的读书方法:
- 记录进度:用SharedPreferences或数据库记录下载位置
- Range请求:告诉服务器"我从哪里开始下载"
- 随机写入:用RandomAccessFile跳到文件指定位置写入
- 进度管理:及时保存进度,支持暂停/继续
这样即使下载过程中网络中断、手机关机,也能从中断的地方继续,大大提升用户体验!