android-async-http文件下载最佳实践:Range请求与进度监听
【免费下载链接】android-async-http 项目地址: https://gitcode.com/gh_mirrors/and/android-async-http
你还在为大文件下载导致的应用卡顿、网络不稳定时的重复下载而烦恼吗?本文将通过实际案例,教你如何利用android-async-http实现断点续传和实时进度监听,解决Android开发中文件下载的常见痛点。读完本文,你将掌握:Range请求分块下载、断点续传实现、下载进度实时更新的完整方案。
核心类与工作原理
RangeFileAsyncHttpResponseHandler
android-async-http提供了RangeFileAsyncHttpResponseHandler专门处理分块下载,其核心机制是:
- 通过HTTP Range头指定请求字节范围
- 将响应数据直接写入本地文件
- 支持断点续传,自动从已下载位置继续
分块下载流程图
分块下载实现步骤
1. 检查服务器支持情况
在开始分块下载前,需要先验证服务器是否支持Range请求:
// 发送HEAD请求获取文件信息
client.head(this, URL, null, new TextHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, String responseString) {
boolean supportsRange = false;
long fileSize = -1;
// 解析响应头信息
for (Header header : headers) {
if ("Accept-Ranges".equals(header.getName())) {
supportsRange = true;
} else if ("Content-Length".equals(header.getName())) {
fileSize = Long.parseLong(header.getValue());
}
}
if (supportsRange && fileSize > 0) {
// 服务器支持Range请求,开始分块下载
startChunkedDownload(fileSize);
} else {
// 服务器不支持Range请求,使用普通下载
startNormalDownload();
}
}
});
2. 实现分块下载逻辑
使用RangeFileAsyncHttpResponseHandler实现分块下载:
private static final int CHUNK_SIZE = 10240; // 10KB分块大小
private void startChunkedDownload(long totalFileSize) {
File downloadFile = new File(getCacheDir(), "downloaded_file.jpg");
RangeFileAsyncHttpResponseHandler responseHandler = new RangeFileAsyncHttpResponseHandler(downloadFile) {
@Override
public void onSuccess(int statusCode, Header[] headers, File file) {
// 检查是否下载完成
if (file.length() < totalFileSize) {
// 未完成,继续下载下一块
sendNextRangeRequest();
} else {
// 下载完成
runOnUiThread(() -> Toast.makeText(context, "下载完成", Toast.LENGTH_SHORT).show());
}
}
@Override
public void onFailure(int statusCode, Header[] headers, Throwable e, File file) {
Log.e("Download", "下载失败: " + e.getMessage());
}
@Override
public void updateRequestHeaders(HttpUriRequest uriRequest) {
super.updateRequestHeaders(uriRequest);
// 设置Range请求头,从上次下载位置继续
long currentSize = downloadFile.length();
uriRequest.setHeader("Range", "bytes=" + currentSize + "-" + (currentSize + CHUNK_SIZE - 1));
}
};
// 执行下载请求
AsyncHttpClient client = new AsyncHttpClient();
client.get(downloadUrl, responseHandler);
}
断点续传完整案例
ResumeDownloadSample实现
ResumeDownloadSample演示了如何实现断点续传功能:
public class ResumeDownloadSample extends SampleParentActivity {
private File downloadTarget;
private File getDownloadTarget() {
try {
if (downloadTarget == null) {
// 创建临时文件存储下载内容
downloadTarget = File.createTempFile("download_", "_resume", getCacheDir());
}
} catch (IOException e) {
Log.e("ResumeDownload", "创建临时文件失败", e);
}
return downloadTarget;
}
@Override
public ResponseHandlerInterface getResponseHandler() {
return new RangeFileAsyncHttpResponseHandler(getDownloadTarget()) {
@Override
public void onSuccess(int statusCode, Header[] headers, File file) {
addView(getColoredView(LIGHTGREEN, "下载进度: " + file.length() + " bytes"));
}
@Override
public void onFailure(int statusCode, Header[] headers, Throwable throwable, File file) {
addView(getColoredView(RED, "下载中断: " + file.length() + " bytes"));
}
};
}
@Override
public String getDefaultHeaders() {
return "Range=bytes=10-20"; // 自定义起始下载位置
}
}
下载进度监听实现
实时进度更新
通过重写RangeFileAsyncHttpResponseHandler的onProgress方法实现进度监听:
@Override
public void onProgress(long bytesWritten, long totalSize) {
super.onProgress(bytesWritten, totalSize);
// 计算下载进度百分比
int progress = (int) (bytesWritten * 100 / totalSize);
// 在UI线程更新进度条
runOnUiThread(() -> progressBar.setProgress(progress));
Log.d("Download", "进度: " + progress + "%");
}
进度监听完整代码
结合分块下载的进度监听实现:
RangeFileAsyncHttpResponseHandler responseHandler = new RangeFileAsyncHttpResponseHandler(downloadFile) {
// ... 其他重写方法 ...
@Override
public void onProgress(long bytesWritten, long totalSize) {
super.onProgress(bytesWritten, totalSize);
// 注意:totalSize是当前分块的大小,不是整个文件的大小
long downloadedSoFar = downloadFile.length() + bytesWritten;
int overallProgress = (int) (downloadedSoFar * 100 / totalFileSize);
updateProgressUI(overallProgress);
}
private void updateProgressUI(int progress) {
runOnUiThread(() -> {
progressBar.setProgress(progress);
progressText.setText(progress + "%");
});
}
};
高级配置与优化
1. 分块大小优化
分块大小(CHUNK_SIZE)的设置需要平衡:
- 太小:增加网络请求次数
- 太大:单次请求时间过长,影响用户体验
建议设置为10KB-1MB,具体取决于应用场景:
// 根据网络类型动态调整分块大小
private int getOptimalChunkSize(NetworkInfo networkInfo) {
if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
return 1024 * 1024; // WiFi环境下使用1MB分块
} else {
return 10 * 1024; // 移动网络下使用10KB分块
}
}
2. 超时与重试策略
配置下载超时和重试机制:
AsyncHttpClient client = new AsyncHttpClient();
client.setTimeout(30000); // 30秒超时
client.setMaxRetriesAndTimeout(3, 5000); // 最多重试3次,每次间隔5秒
// 设置自定义重试处理器
client.setRetryHandler(new RetryHandler() {
@Override
public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
// 仅在网络异常时重试
return executionCount <= 3 && (exception instanceof SocketException || exception instanceof ConnectException);
}
});
完整示例代码
RangeResponseSample提供了完整的分块下载实现,核心代码如下:
public class RangeResponseSample extends GetSample {
private static final int CHUNK_SIZE = 10240; // 10KB分块
private File file;
private long fileSize = -1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
// 创建临时文件
file = File.createTempFile("temp_", "_handled", getCacheDir());
} catch (IOException e) {
Log.e("RangeDownload", "创建临时文件失败", e);
}
}
@Override
public ResponseHandlerInterface getResponseHandler() {
return new RangeFileAsyncHttpResponseHandler(file) {
@Override
public void onSuccess(int statusCode, Header[] headers, File file) {
if (fileSize < 1) {
// 解析文件总大小
for (Header header : headers) {
if ("Content-Length".equals(header.getName())) {
fileSize = Long.parseLong(header.getValue());
}
}
}
// 检查是否需要继续下载
if (file.length() < fileSize) {
sendNextRangeRequest();
} else {
Toast.makeText(RangeResponseSample.this, "下载完成", Toast.LENGTH_SHORT).show();
}
}
@Override
public void updateRequestHeaders(HttpUriRequest uriRequest) {
super.updateRequestHeaders(uriRequest);
long currentSize = file.length();
uriRequest.setHeader("Range", "bytes=" + currentSize + "-" + (currentSize + CHUNK_SIZE - 1));
}
};
}
}
注意事项与最佳实践
- 权限申请:确保在AndroidManifest.xml中添加网络和存储权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-
主线程处理:所有UI更新必须在主线程执行,使用runOnUiThread或Handler
-
临时文件管理:下载完成后及时清理临时文件,参考RangeResponseSample的onDestroy方法:
@Override
protected void onDestroy() {
super.onDestroy();
// 移除临时文件
if (file != null && !file.delete()) {
Log.e("Download", "无法删除临时文件: " + file.getAbsolutePath());
}
}
- 服务器兼容性检查:在使用Range请求前,务必验证服务器是否支持该功能
总结与扩展
通过本文介绍的方法,你可以实现高效可靠的文件下载功能,支持断点续传和进度监听。android-async-http还提供了更多高级功能:
- PersistentCookieStore:持久化Cookie存储
- RequestParams:丰富的请求参数构建
- AsyncHttpClient:灵活的客户端配置
建议结合官方示例sample目录下的完整代码,进一步探索更多高级用法。你还可以扩展实现:多任务下载队列、后台下载服务、下载完成通知等功能,打造更完善的下载体验。
如果觉得本文对你有帮助,欢迎点赞收藏,关注作者获取更多Android网络编程实践技巧!下一篇将介绍android-async-http的缓存策略与性能优化。
【免费下载链接】android-async-http 项目地址: https://gitcode.com/gh_mirrors/and/android-async-http
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



