Android学习(54) -- 多线程下载 原理和代码
来源:互联网 发布:大数据课程体系 编辑:程序博客网 时间:2024/06/05 01:54
多线程下载
原理:服务器CPU分配给每条线程的时间片相同,服务器带宽平均分配给每条线程,所以客户端开启的线程越多,就能抢占到更多的服务器资源
单线程下载:从输入流第0个字节开始读取,读取到最后一个字节,把读取到的数据写到本地文件中,
写的时候也要从文件的第0个位置开始写,写到最后一个位置多线程的计算:
每个线程预下载的大小: size = 总的大小/线程的数量 (注意,最后一个问题) size = 10 / 3id=0线程: 0 -- 2id=1线程: 3 -- 5id=2线程: 6 -- 9 (多余的大小可以给最后一个线程 )开始位置: start = 线程编号 * 每个线程预下载的大小 start = id * size结束位置: end =(id+1)*size -1 最后一个线程结束位置:总的大小-1 length-1
(使用Java项目进行多线程下载测试,因android比较麻烦,
Java中搞定移植到android中即可,测试下载的时候最好使用可执行程序,
比如.exe程序,这样可以知道是否可以真成功)
确定每条线程下载多少数据
发送http请求至下载地址
String path = "http://192.168.1.13:8080/QQ.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 id = 1; id <= 3; id++){ //计算每个线程下载数据的开始位置和结束位置 int startIndex = (id - 1) * blockSize; int endIndex = id * blockSize - 1; if(id == THREAD_COUNT){ endIndex = length; } //开启线程,按照计算出来的开始结束位置开始下载数据 new DownLoadThread(startIndex, endIndex, id).start(); }
再次发送请求至下载地址,请求开始位置至结束位置的数据
String path = "http://192.168.1.13:8080/QQ.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(); }
核心代码
public class MultiDownload {static int ThreadCount = 3;static int finishedThread = 0;//确定下载地址static String path = "http://192.168.1.13:8080/QQ.exe";public static void main(String[] args) { //发送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(); File file = new File("local_qq.exe"); //生成临时文件--占用磁盘空间--可以防止磁盘空间不够用 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(); } }}class DownLoadThread extends Thread{ int startIndex; //下载的开始位置 int endIndex; //下载的结束位置 int threadId; //下载的线程Idpublic DownLoadThread(int startIndex, int endIndex, int threadId) { super(); this.startIndex = startIndex; this.endIndex = endIndex; this.threadId = threadId;}@Overridepublic void run() { //再次发送http请求,下载原文件 try { File progressFile = new File(threadId + ".txt"); //判断进度临时文件是否存在 if(progressFile.exists()){ FileInputStream fis = new FileInputStream(progressFile); BufferedReader br = new BufferedReader(new InputStreamReader(fis)); //从进度临时文件中读取出上一次下载的总进度,然后与原本的开始位置相加,得到新的开始位置 startIndex += Integer.parseInt(br.readLine()); fis.close(); } System.out.println("线程" + threadId + "的下载区间是:" + startIndex + "---" + endIndex); HttpURLConnection conn; URL url = new URL(MultiDownload.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("local_qq.exe"); 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); //生成一个专门用来记录下载进度的临时文件 RandomAccessFile progressRaf = new RandomAccessFile(progressFile, "rwd"); //每次读取流里数据之后,同步把当前线程下载的总进度写入进度临时文件中 progressRaf.write((total + "").getBytes()); progressRaf.close(); } System.out.println("线程" + threadId + "下载完毕-------------------"); raf.close(); MultiDownload.finishedThread++; synchronized (MultiDownload.path) { if(MultiDownload.finishedThread == MultiDownload.ThreadCount){ for (int i = 0; i < MultiDownload.ThreadCount; i++) { File f = new File(i + ".txt"); f.delete(); } MultiDownload.finishedThread = 0; } } } } catch (Exception e) { e.printStackTrace(); }}}
0 0
- Android学习(54) -- 多线程下载 原理和代码
- Android学习笔记---java实现多线程下载器,30_多线程下载原理介绍和使用
- Android 中实现多线程下载和断点续传的原理和代码
- android多线程下载原理
- Android多线程下载原理
- Android:网络:多线程下载(原理)
- 多线程下载,断点续传原理解析和代码实现
- http协议学习-断点续传和多线程下载的实现原理
- Android中 多线程下载原理
- Android学习 多线程 下载
- java(android)多线程下载,和多线程断点下载
- android 多线程应用 下载代码
- 多线程下载原理及核心代码
- 多线程下载原理及核心代码
- android学习笔记---31_多线程断点下载器,下载原理实现
- -多线程下载和多线程断点下载的原理-
- Java 多线程分段下载原理分析和实现(转载)
- Android实现网络多线程断点续传下载原理
- poj2506Tiling
- 博客第一篇:C语言处理动态数组
- 生活感悟
- mac 下 MyEclipse 2015 安装并且激活
- ACM 数论 HDU1215 七夕节 (模板!)
- Android学习(54) -- 多线程下载 原理和代码
- 基于PHP的sso单点登录实例
- adb无线调试
- 使用java访问https链接时,不信任证书导致PKIX path building failed
- Eclipse乱码解决方案总结(UTF-8 <---> GBK)
- iOS 网络篇6—NSURLConnection基本使用
- html中无法运行php代码
- ACM 数论 hdu 1492 The number of divisors(约数) about Humble Numbers
- linux 设备与驱动的绑定