多线程断点续传无论在什么平台上都是极为重要的,这部分知识非常重要。老规矩,用一张图来介绍今天的内容。
图片看不清的话可以右键新窗口打开
直接看代码吧
public class MainActivity extends Activity {
String path = "http://192.168.15.77:8080/QQPlayer.exe";
int threadCount = 3;
int finishedThreadCount = 0;
int currentPbProgress;
private ProgressBar pb;
private TextView tv;
Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
tv.setText((long)pb.getProgress() * 100 / pb.getMax() + "%");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//进度条用于显示当前下载进度,下载的总字节数作为progress,目标文件的总大小作为max
pb = (ProgressBar) findViewById(R.id.pb);
tv = (TextView) findViewById(R.id.tv);
}
public void click(View v){
Thread t = new Thread(){
@Override
public void run() {
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
if(conn.getResponseCode() == 200){
//拿到要下载的文件的总长度
int length = conn.getContentLength();
//在本地生成一个临时文件,临时文件大小与目标文件大小一致
File file= new File(Environment.getExternalStorageDirectory(), getFileName(path));
RandomAccessFile raf = new RandomAccessFile(file, "rwd");
raf.setLength(length);
raf.close();
//设置进度条的总进度为目标文件总长
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(startIndex, endIndex, i).start();
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t.start();
}
String getFileName(String path){
int index = path.lastIndexOf("/");
return path.substring(index + 1);
}
class DownLoadThread extends Thread{
int startIndex;
int endIndex;
int threadId;
public DownLoadThread(int startIndex, int endIndex, int threadId) {
super();
this.startIndex = startIndex;
this.endIndex = endIndex;
this.threadId = threadId;
}
@Override
public void run() {
//开启子线程下载目标文件
try {
File fileProgess = new File(Environment.getExternalStorageDirectory(), threadId + ".txt");
int lastTotal = 0;
//判断文本临时文件是否存在
if(fileProgess.exists()){
FileInputStream fis = new FileInputStream(fileProgess);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
//获取上一下载的进度
lastTotal = Integer.parseInt(br.readLine());
//改变下载的开始位置,已经下载过的数据,就不要再去请求了
startIndex += lastTotal;
fis.close();
//把上一次下载的总进度写入进度条
currentPbProgress += lastTotal;
pb.setProgress(currentPbProgress);
handler.sendEmptyMessage(0);
}
System.out.println("线程" + threadId + "的最终下载区间为:" + startIndex + "~" + endIndex);
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
//定义请求的数据的范围
conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
if(conn.getResponseCode() == 206){
//流里的数据只有startIndex到endIndex区间的数据,并不会包含目标文件所有数据
InputStream is = conn.getInputStream();
byte[] b = new byte[1024];
int len;
int total = lastTotal;
File file= new File(Environment.getExternalStorageDirectory(), getFileName(path));
RandomAccessFile raf = new RandomAccessFile(file, "rwd");
//改变往raf中写入数据的开始位置
raf.seek(startIndex);
while((len = is.read(b)) != -1){
total += len;
System.out.println("线程" + threadId + "已下载的字节数为:" + total);
raf.write(b, 0, len);
//为了完成断点续传,while循环每次下载的进度,都写入一个文本临时文件中
RandomAccessFile rafProgress = new RandomAccessFile(fileProgess, "rwd");
rafProgress.write((total + "").getBytes());
rafProgress.close();
currentPbProgress += len;
//所有线程每次下载len个长度的字节,都会写到进度条的总进度中
pb.setProgress(currentPbProgress);
handler.sendEmptyMessage(0);
}
System.out.println("线程" + threadId + "下载完毕---------------------");
raf.close();
finishedThreadCount++;
//三个线程全部下载完毕,才去删除文本临时文件
synchronized (path) {
if(finishedThreadCount == 3){
for (int i = 0; i < threadCount; i++) {
File f = new File(Environment.getExternalStorageDirectory(), i + ".txt");
if(f.exists())
f.delete();
}
finishedThreadCount = 0;
}
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
原文:http://blog.csdn.net/jinfulin/article/details/45155359