[android] 05_多线程下载JavaSe版本和Android版本

Android 4.0

多线程下载 

多线程下载原理:

多线程下载步骤:

1)开发多线程最好不要在android中开发,android中多线程部署和调试很麻烦,最好用javase工程
2)进度条和进度框可以在子线程中更新ui,底层实现了

1、建立一个Java Peoject
2、和服务器建立HTTP连接,获取要下载内容的总大小contentLength
3、开启threadCount个线程,计算每个线程下载的大小threadSize = contentLength/threadCount
4、用RandomAccessFile raf在本地建立一个与服务器大小一致的文件
5、通过设置conn.setRequestProperty("range","bytes=1594-47143");设置每个线程下载的范围
6、通过raf.seek(int) 定位到指定的位置下载。
7、还可以记录下载文件的位置
8、第二次下载时,检查是否有记录文件,如果有,那么读取下载的位置,继续下载
9、下载完毕后,将记录文件删除

同步/异步.
 
多次调用异步方法会导致什么问题?
 
同步 在同一个线程里面执行的.
异步 开启新的线程

核心代码:
javase版本

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
public class MultiDownload {
    private static String path = "Http://192.168.221.221:8080/web/e.exe";
    private static int threadCount = 7;// 下载的线程的个数
    private static int livingThread = 0;// 记住活着的线程-还在下载的线程
    public static void main(String[] args) throws IOException {
        URL url = new URL(path);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("GET");
        connection.setConnectTimeout(5000);
        int code = connection.getResponseCode();
        System.out.println("1:服务器返回状态码:" + code);
        if (code == 200) {
            // 1、服务器文件的长度
            int contentLength = connection.getContentLength();
            System.out.println("文件的总长度:" + contentLength);
            //2、每一个线程下载的大小
            int blockSize = contentLength/threadCount;
            System.out.println("每个线程下载的大小blockSize:" + blockSize);
            for (int i = 1; i <= threadCount; i++) {
                // 3、每一个线程的起始位置和结束位置
                int startIndex = (i - 1) * blockSize;
                int endIndex = i * blockSize - 1;
                if (i == threadCount) {
                    endIndex = contentLength;// 4、最后一个结束位置为文件末尾
                }
                livingThread = threadCount;
                // 5、开启线程下载文件
                new Thread(new DownloadTask(i, startIndex, endIndex)).start();
            }
        }
    }
    /**
     * 根据路径获取文件名
     * 
     * @param filepath 文件的路径
     * @return 文件名
     */
    public static String getFileName(String filepath) {
        return filepath.substring(filepath.lastIndexOf("/") + 1);
    }
    
    /**
     * 
     * 线程下载类
     * 
     * @author hacket
     * 
     */
    private static class DownloadTask implements Runnable {
        private int id;// 线程id
        private int startIndex;// 下载起始位置
        private int endIndex;// 下载结束位置
        public DownloadTask(int id, int startIndex, int endIndex) {
            this.id = id;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
        }
        @Override
        public void run() {
            
            try {
                // 一、开启一个http连接
                URL url = new URL(path);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
                connection.setConnectTimeout(5000);
                connection.setRequestProperty("Range""bytes="+startIndex+"-"+endIndex);
                // 五、在每次下载之前,检查是否存在已经下载大小记录的文件
                File f = new File(getFileName(path) + id + ".txt");
                if (f.exists() && f.length() > 0) {// 文件存在
                    BufferedReader br = new BufferedReader(new FileReader(f));
                    String total = br.readLine();
                    startIndex += Integer.parseInt(total);
                    br.close();
                }
                System.out.println("线程id:"+id+"下载位置:"+startIndex+"——"+endIndex);
                
                // 二、创建一个随机访问文件
                RandomAccessFile raf = new RandomAccessFile(getFileName(path), "rwd");
                raf.setLength(connection.getContentLength());// 设置文件总长度
                raf.seek(startIndex);// 设置每个线程下载文件的开始位置
                // 三、获取输入流开启文件的下载
                InputStream in = connection.getInputStream();
                int len = 0;
                byte[] buf = new byte[1024];
                int total = 0;// 记录当前线程下载文件的大小
                while ((len = in.read(buf)) != -1) {
                    // 四、记录当前线程下载的大小
                    File file = new File(getFileName(path) + id + ".txt");
                    raf.write(buf, 0, len);
                    RandomAccessFile positionAccessFile = new RandomAccessFile(file, "rwd");
                    total += len;
                    // 将当前下载的大小保存到一个文件中去
                    positionAccessFile.write(String.valueOf(total).getBytes());
                    positionAccessFile.close();
                }
                raf.close();
                in.close();
                System.out.println("线程:" + id + "下载完毕。。。");
                
            } catch (IOException e) {
                e.printStackTrace();
            }finally{
                synchronized (DownloadTask.class) {
                    livingThread--;
                    System.out.println("livingThread:" + livingThread);
                    if (livingThread <= 0) {
                        System.out.println("全部下载完毕");
                        // 六、下载完毕后,将记录文件给删除
                        for (int i = 1; i <= threadCount; i++) {
                            File deletFile = new File(getFileName(path) + i
                                    + ".txt");
                                System.out.println(i+"删除:"+deletFile.delete());
                        }
                    }
                }
            }
        }
    }
}