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
原创粉丝点击