在上一篇文章 Java、Android中的多线程异步下载 中并没有完成断点续传的功能。
所谓的断点续传,主要就是使用一个记录下载进度的文件,每次下载开始时,先判断进度文件是否存在,并读取其中的进度。
下面的代码就是在Android上实现短线续传下载功能的代码:
package test.multdownload;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import com.fc.rssreader.R;
public class MultDownloadActivity extends Activity {
private Button downloadButton;
private ProgressBar pb;
public int threadCount = 3;
public String path = "http://dldir1.qq.com/qqfile/qq/QQ7.2/14810/QQ7.2.exe";
// public String
// path="http://hiphotos.baidu.com/240728057/pic/item/6a50e38242aad8f60cf4d2b3.jpg";
int threadFinish = 0;
int currentProgress=0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mult_download);
downloadButton = (Button) findViewById(R.id.download);
pb = (ProgressBar) findViewById(R.id.pb);
downloadButton.setOnClickListener(listener);
}
private OnClickListener listener = new Button.OnClickListener() {
@Override
public void onClick(View v) {
Thread t = new Thread() {
public void run() {
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
conn.setRequestMethod("GET");
conn.setReadTimeout(5000);
conn.setConnectTimeout(5000);
if (conn.getResponseCode() == 200) {
// 下载文件的大小
int length = conn.getContentLength();
// 创建临时文件的路径和名称
File file = new File("sdcard/"+getFileName(path));
// 创建速记存储文件对象
RandomAccessFile raf = new RandomAccessFile(file,
"rwd");
// 设置临时文件大小
raf.setLength(length);
//给进度条的最大进度设置原文件总大小
pb.setMax(length);
// 计算每个线程下载的字节数
int size = length / threadCount;
for (int i = 0; i < threadCount; i++) {
// 计算每个线程下载数据的开始和结束位置
int startIndex = i * size;
int endIndex = (i + 1) * size - 1;
// 如果是最后一个线程,那么借宿位置特殊处理
if (i == threadCount - 1) {
endIndex = length - 1;
}
System.out.println("线程" + i + "已经启动,下载位置为:"
+ startIndex + "--------->" + endIndex);
// 开始线程
new DownloadThread(i, startIndex, endIndex, url)
.start();
}
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
};
};
t.start();
}
};
public static String getFileName(String path) {
int index = path.lastIndexOf("/");
return path.substring(index + 1);
}
/**
* @author frozen cloud 下载线程
*/
class DownloadThread extends Thread {
int threadId;
int startIndex;
int endIndex;
URL url;
public DownloadThread(int threadId, int startIndex, int endIndex,
URL url) {
super();
this.threadId = threadId;
this.startIndex = startIndex;
this.endIndex = endIndex;
this.url = url;
}
@Override
public void run() {
try {
// 设置记录下载进度的临时文件的路径和名称
File fileProgress = new File("sdcard/"+threadId + ".txt");
// 判断保存下载进度的临时文件是否存在
if (fileProgress.exists()) {
FileInputStream fis = new FileInputStream(fileProgress);
BufferedReader br = new BufferedReader(
new InputStreamReader(fis));
int newStartIndex = Integer.parseInt(br.readLine());
currentProgress += (newStartIndex - startIndex);
pb.setProgress(currentProgress);
startIndex = newStartIndex;
fis.close();
}
System.out.println("线程" + threadId + "最终开始位置:" + startIndex);
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
conn.setRequestMethod("GET");
conn.setReadTimeout(5000);
conn.setConnectTimeout(5000);
// 设置请求的数据的范围
conn.setRequestProperty("Range", "bytes=" + startIndex + "-"
+ endIndex);
if (conn.getResponseCode() == 206) {
InputStream is = conn.getInputStream();
int len = 0;
byte[] b = new byte[1024];
int total = 0;
// 创建临时文件的路径和名称
File file = new File("sdcard/"+getFileName(path));
// 创建速记存储文件对象
RandomAccessFile raf = new RandomAccessFile(file, "rwd");
// 设置线程写入数据的开始位置
raf.seek(startIndex);
// 记录当前下载进度
int currentPosition = startIndex;
while ((len = is.read(b)) != -1) {
// 下载文件
raf.write(b, 0, len);
// 下载进度
total += len;
RandomAccessFile rafProgress = new RandomAccessFile(
fileProgress, "rwd");
currentPosition = startIndex + total;
rafProgress.write((currentPosition + "").getBytes());
rafProgress.close();
// System.out.println("线程"+threadId+"总共下载了:"+total);
//每次下载len个长度的字节,都把字节设置到进度条的进度中
currentProgress += len;
pb.setProgress(currentPosition);
}
raf.close();
System.out.println("线程下载结束:" + threadId);
threadFinish++;
// 如果这个条件成立,说明所有线程下载完毕
synchronized (path) {
if (threadFinish == threadCount) {
for (int i = 0; i < threadCount; i++) {
File temp = new File("sdcard/"+i + ".txt");
temp.delete();
}
threadFinish = 0;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
整个代码与 Java、Android中的多线程异步下载中代码的主要区别就是多了一个进度文件的处理功能。
在这个例子中每个线程都生成了一个自己的进度文件,下载完成则删除进度文件,在实际中,比方说迅雷下载的时候,这个文件只生成一个的。
由于多个文件的存在,会产生一些问题,必须同步删除进度文件。
以上代码基本上把断点续传的原理都展示的很清楚了。
工作的流程如图所示。