Andorid多线程断点续传下载
来源:互联网 发布:数据库应用软件开发 编辑:程序博客网 时间:2024/05/18 07:06
多线程断点续传下载
多线程原理
- 服务器给每条线程分配的时间片段相同,并且平均分配带宽,所以客户端开启的线程越多,就能抢占到更多的服务器资源。
确定每条线程下载多少数据
- 发送请求到服务器
String path = "http://192.168.13.13/pal.exe"; URL url =new URL(path); HttpURLConnection conn =(HttpURLConnection)url.openConnection(); conn.setReadTimeOut(5000); conn.setConnectTimeout(5000); conn.setRequestMethod("GET");
- 获取文件总长度,然后创建长度一致的临时文件
if(conn.getResponseCode()==200){ //获取服务器流中数据的长度 int length = conn.getContentLength(); //创建一个临时文件并存储下载数据 RandomAccessFile raf = new RandomAccessFile(getFileName(path),"rwd"); //设置临时文件的大小 raf.setLength(length); raf.close(); //计算每个线程下载多少数据 int blockSize = length / THREAD_COUNT;
计算每条线程下载数据的开始位置和结束位置
- 注意最后一个线程的结束
for(int i=0;i<THREAD_COUNT;i++){ //计算每个线程下载数据的开始位置和结束位置 int startIndex = i* blockSize; int endIndex = (i +1)* blockSize - 1; if(id == THREAD_COUNT-1){ endIndex = length-1; } //开启线程,按照计算出来的开始结束位置开始下载数据 new DownLoadThread(startIndex, endIndex, id).start(); } }
再次发送请求至下载地址
- 请求开始位置至结束位置的数据
String path = "http://192.168.1.102:8080/editplus.exe"; URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setReadTimeout(5000); conn.setConnectTimeout(5000); conn.setRequestMethod("GET"); //向服务器请求部分数据设置 conn.setRequestProperty("Range","bytes="+startIndex+"-"+endIndex); conn.connect();
- 下载请求到的数据,放置到临时文件中
if(conn.getResponseCode()==206){ InputStream is =conn.getInputStream(); RandomAccessFile raf = new RandomAccessFile(getFileName(path),"rwd"); //指定从哪个位置开始存放数据 raf.seek(startIndex); byte[] b = new byte[1024]; int len; while((len = is.read(b)) != -1){ raf.write(b, 0, len); } raf.close(); }
带断点续传功能的多线程下载
- 定义一个int变量记录每条线程下载的数据总长度,然后加上该线程的下载开始位置,得到的结果就是下次下载时,该线程的开始位置,把得到的结果存入缓存文件
//用来记录当前线程的下载长度 int total = 0; while((len=is.read(b))!=-1){ raf.write(b,0,len); total+=len; //每次系在都把最新的下载位置写入缓存文本文件 File file =new File(threadId+".txt"); RandomAccessFile progressRaf=new RandomAccessFile(progressFile,"rwd"); progressRaf.write((startIndex+total+"").getBytes()); progressRaf.close(); }
- 下次开始时,先读取缓存文件中的值,得到的值就是线程新的开始位置
FileInputStream fis = new FileInputStream(file); BufferedRead br =new BufferedRead(new InputStreamRead(fis)); String text = br.readLine(); int newStartIndex = Ingeter.parseInt(text); //把读取到的值作为新的开始位置 startIndex = newStartIndex; fis.cloes();
- 三条线程都下载完成以后,删除缓存文件
finalThread++; synchronized(path){ if(RUNNING_THREAD == 0){ for(int i = 0; i <= 3; i++){ File f = new File(i + ".txt"); f.delete(); } } }
手机版的断点续传多线程下载器
- 把刚才的代码直接粘贴过来就能用,记得在访问文件时的路径要改成Android的目录,添加访问网络和外部存储的路径
用进度条显示下载进度
- 拿到下载文件总长度时,设置进度条的最大值
//设置进度条的最大值 pb.setMax(length);
- 进度条需要显示三条线程的整体下载进度,所以三条线程每下载一次,就要把新下载的长度加入进度条
- 定义一个int全局变量,记录三条线程的总下载长度
int progress;
- 刷新进度条
while((len = is.read(b)) != -1){ raf.write(b, 0, len); //把当前线程本次下载的长度加到进度条里 progress += len; pb.setProgress(progress); }
- 每次断点下载时,从新的开始位置开始下载,进度条也要从新的位置开始显示,在读取缓存文件获取新的下载开始位置时,也要处理进度条进度
FileInputStream fis = new FileInputStream(file); BufferedReader br = new BufferedReader(new InputStreamReader(fis)); String text = br.readLine(); int newStartIndex = Integer.parseInt(text); //新开始位置减去原本的开始位置,得到已经下载的数据长度 int alreadyDownload = newStartIndex - startIndex; //把已经下载的长度设置入进度条 progress += alreadyDownload;
添加文本框显示百分比进度
tv.setText(progress * 100 / pb.getMax() + "%");
HttpUtils的使用
HttpUtils本身就支持多线程断点续传,使用起来非常的方便
- 创建HttpUtils对象
HttpUtils http = new HttpUtils();
- 下载文件
http.download(url, //下载请求的网址 target, //下载的数据保存路径和文件名 true, //是否开启断点续传 true, //如果服务器响应头中包含了文件名,那么下载完毕后自动重命名 new RequestCallBack<File>() {//侦听下载状态 //下载成功此方法调用 @Override public void onSuccess(ResponseInfo<File> arg0) { tv.setText("下载成功" + arg0.result.getPath()); } //下载失败此方法调用,比如文件已经下载、没有网络权限、文件访问不到,方法传入一个字符串参数告知失败原因 @Override public void onFailure(HttpException arg0, String arg1) { tv.setText("下载失败" + arg1); } //在下载过程中不断的调用,用于刷新进度条 @Override public void onLoading(long total, long current, boolean isUploading) { super.onLoading(total, current, isUploading); //设置进度条总长度 pb.setMax((int) total); //设置进度条当前进度 pb.setProgress((int) current); tv_progress.setText(current * 100 / total + "%"); } });
示例
package com.hyman.mobilemultidownload; 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.os.Bundle; import android.os.Environment; import android.os.Handler; import android.app.Activity; import android.view.Menu; import android.view.View; import android.widget.ProgressBar; import android.widget.TextView; public class MainActivity extends Activity { static int ThreadCount = 3; static int finishedThread = 0; int currentProgress; String fileName = "QQPlayer.exe"; //确定下载地址 String path = "http://192.168.13.13:8080/" + fileName; private ProgressBar pb; TextView tv; Handler handler = new Handler(){ public void handleMessage(android.os.Message msg) { //把变量改成long,在long下运算 tv.setText((long)pb.getProgress() * 100 / pb.getMax() + "%"); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); 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() { //发送get请求,请求这个地址的资源 try { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); if(conn.getResponseCode() == 200){ //拿到所请求资源文件的长度 int length = conn.getContentLength(); //设置进度条的最大值就是原文件的总长度 pb.setMax(length); File file = new File(Environment.getExternalStorageDirectory(), fileName); //生成临时文件 RandomAccessFile raf = new RandomAccessFile(file, "rwd"); //设置临时文件的大小 raf.setLength(length); raf.close(); //计算出每个线程应该下载多少字节 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(); } 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() { //再次发送http请求,下载原文件 try { File progressFile = new File(Environment.getExternalStorageDirectory(), threadId + ".txt"); //判断进度临时文件是否存在 if(progressFile.exists()){ FileInputStream fis = new FileInputStream(progressFile); BufferedReader br = new BufferedReader(new InputStreamReader(fis)); //从进度临时文件中读取出上一次下载的总进度,然后与原本的开始位置相加,得到新的开始位置 int lastProgress = Integer.parseInt(br.readLine()); startIndex += lastProgress; //把上次下载的进度显示至进度条 currentProgress += lastProgress; pb.setProgress(currentProgress); //发送消息,让主线程刷新文本进度 handler.sendEmptyMessage(1); fis.close(); } System.out.println("线程" + threadId + "的下载区间是:" + startIndex + "---" + endIndex); HttpURLConnection conn; URL url = new URL(path); conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); //设置本次http请求所请求的数据的区间 conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex); //请求部分数据,相应码是206 if(conn.getResponseCode() == 206){ //流里此时只有1/3原文件的数据 InputStream is = conn.getInputStream(); byte[] b = new byte[1024]; int len = 0; int total = 0; //拿到临时文件的输出流 File file = new File(Environment.getExternalStorageDirectory(), fileName); RandomAccessFile raf = new RandomAccessFile(file, "rwd"); //把文件的写入位置移动至startIndex raf.seek(startIndex); while((len = is.read(b)) != -1){ //每次读取流里数据之后,同步把数据写入临时文件 raf.write(b, 0, len); total += len; System.out.println("线程" + threadId + "下载了" + total); //每次读取流里数据之后,把本次读取的数据的长度显示至进度条 currentProgress += len; pb.setProgress(currentProgress); //发送消息,让主线程刷新文本进度 handler.sendEmptyMessage(1); //生成一个专门用来记录下载进度的临时文件 RandomAccessFile progressRaf = new RandomAccessFile(progressFile, "rwd"); //每次读取流里数据之后,同步把当前线程下载的总进度写入进度临时文件中 progressRaf.write((total + "").getBytes()); progressRaf.close(); } System.out.println("线程" + threadId + "下载完毕-------------------"); raf.close(); finishedThread++; synchronized (path) { if(finishedThread == ThreadCount){ for (int i = 0; i < ThreadCount; i++) { File f = new File(Environment.getExternalStorageDirectory(), i + ".txt"); f.delete(); } finishedThread = 0; } } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }}
0 0
- Andorid多线程断点续传下载
- andorid单线程断点续传下载
- 多线程断点续传后台下载
- 多线程断点续传后台下载
- android 多线程断点续传下载
- 多线程断点续传后台下载
- 多线程并行下载,断点续传
- Android多线程.断点续传下载
- android 多线程断点续传下载
- Android 多线程下载断点续传
- Java多线程断点续传下载
- 多线程断点续传后台下载
- Android多线程断点续传下载
- 多线程断点续传下载Demo
- 多线程下载 断点续传
- 多线程断点续传下载
- 多线程断点续传下载。
- Android多线程断点续传下载
- Cocos2D物理碰撞不按预期工作的排查工作
- 黑马程序员——面试题小总结2
- 单点登录
- 关于UINavigationController与UITableView聚合的发现
- ARM assembler in Raspberry Pi – Chapter 3
- Andorid多线程断点续传下载
- 【软/自考】算法实用技巧——递归VS迭代
- while判断值为cin输入值时,注意调试时要用文件结束符结束循环
- PHP:Fatal error: Class 'COM' not found in … 的处理办法
- free(): invalid pointer
- Linux抓包
- C++之文件重定向 通过txt读入数据和txt写数据
- Ubuntu安装bochs
- KVC中setValuesForKeysWithDictionary: