15.2-多线程下载

原理:

在这里插入图片描述
多线程下载就是分为多个线程去下载文件,每个线程对应文件的每段数据的下载模块,每个文件的片段只有一个线程去处理。
假如:一个文件长度为10,被三个线程切分,最终线程的分配每段的大小就是3、3、4,这些都是每个线程要下载的文件大小。

带着疑问:
1.如何获取文件的大小
2.如何获取分段的网络数据?
3.如何分割下载的数据?
4.如何把多段网络数据写入到一个文件里?RadomAccessFile
5.如何判断整个文件是否下载完成?

线程工具类:

获取文件长度,根据线程数量切分每个线程下载的区间

代码实现

 
    /**
     * 开始下载
     * 1.获取文件的大小
     * 2.构造多个线程的对象
     * fileurl 下载文件的服务器地址
     * maxThreadCount 线程数量
     * filename 下载后文件的名称
     */
    public void startDownLoad(final ProgressBar progressBar, final Context context, final String fileUrl, final int maxThreadCount, String fileName) {

        this.readestTask = maxThreadCount;
        // 创建本地 下载的文件
        final String targetFilePathAndName = Environment.getExternalStorageDirectory() + File.separator + fileName;


        new Thread() {
            @Override
            public void run() {
                super.run();
                try {
                    // 获取文件的长度
                    HttpURLConnection connection = getConnection(fileUrl,0,0);
                    contentLength = connection.getContentLength();
                    connection.disconnect();
                    Log.d(TAG, "doHttpTask: thread=contentLength" + contentLength);

                    // 创建本地的临时文件
                    RandomAccessFile file = new RandomAccessFile(targetFilePathAndName, "rw");
                    file.setLength(contentLength);// 设置本地文件的大小
                    progressBar.setMax(contentLength);
                    file.close();
                    Log.d(TAG, "doHttpTask: thread=" + totalCount);

                    // 线程池的对象创建
                    ExecutorService executorService = Executors.newFixedThreadPool(maxThreadCount);

                    partSize = contentLength / maxThreadCount;

                    for (int x = 0; x < maxThreadCount; x++) {
                        // 创建多个线程,使用线程池管理

                        // 获取 每段文件的下载起点位置
                        int startPos = x * partSize;
                        int endPos = (x+1)*partSize-1;
                        //最后一个线程,下载剩余的文件大小。
                        if (x == maxThreadCount - 1) {
                            endPos = contentLength-1;
                        }

                        DownLoadTask downLoadTask = new DownLoadTask(new DownLoadTask.IUpdateProgress() {
                            @Override
                            public void update(int progress) {

                                progressBar.incrementProgressBy(progress);

                                Log.d(TAG, "update: 进度条==" + progressBar.getProgress() );
                            }
                        }, context, startPos, endPos, fileUrl, targetFilePathAndName);

                        // 开始执行线程的任务
                        executorService.execute(downLoadTask);
                    }

                    executorService.shutdown();


                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }.start();
    }
  public static HttpURLConnection getConnection(String fileUrl, int start, int end) throws IOException {
        URL url = new URL(fileUrl);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setConnectTimeout(10 * 1000);
        conn.setRequestMethod("GET");

        if (start >= 0 && end > 0) {

            ///设置请求部分资源   Range表示随机位置   重点1

            conn.setRequestProperty("Range", "bytes=" + start + "-" + end); //设置当前线程从start开始  到 end结束
        }

        conn.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
        conn.setRequestProperty("Accept-Language", "zh-CN");
        conn.setRequestProperty("Referer", fileUrl);
        conn.setRequestProperty("Charset", "UTF-8");
        conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
        conn.setRequestProperty("Connection", "Keep-Alive");
        conn.setRequestProperty("Accept-Encoding", "identity");
        return conn;
    }

每个线程下载每段数据

public class DownLoadTask implements Runnable {
   
    //下载的起点
    private int startPos;

    public String fileUrl;

    private final int endPos;


    private RandomAccessFile currentPart;

    private static final String TAG = "DownLoadTask";
    IUpdateProgress updateProgress;
    private Context context;
    public int currentDownLoaded;

    /**
     * @param startPos              下载的起点
     * @param endPos              下载的大小
     * @param fileUrl               服务器的文件地址
     * @param targetFilePathAndName 本地文件的路径
     * @throws IOException
     */
    public DownLoadTask(IUpdateProgress updateProgress, Context context, int startPos, int endPos,
                        String fileUrl, String targetFilePathAndName) throws IOException {
        this.updateProgress = updateProgress;
        this.context = context;
        this.startPos = startPos;
        this.fileUrl = fileUrl;
        this.endPos = endPos;
        this.currentPart = currentPart;
       

        // 创建本地的文件,定位到起点位置,便于当前线程从起点位置开始下载
        currentPart = new RandomAccessFile(targetFilePathAndName, "rw");// read write
        currentPart.seek(startPos);
    }

    @Override
    public void run() {

        downItemTask();
    }

     
    private void downItemTask() {

        try {
            HttpURLConnection connection = MoreThreadUtils.getConnection(fileUrl,startPos,endPos);
            InputStream in = connection.getInputStream();

            byte[] bytes = new byte[1024];
            int hasRead;
            // 开始读取流的操作
            while ((hasRead = in.read(bytes)) > 0) {
                currentPart.write(bytes, 0, hasRead);// 写入数据到本地文件
                if (updateProgress!=null){
                    updateProgress.update(hasRead);
                }
//                Thread.sleep(50);
            }
            Log.d(TAG, Thread.currentThread() + "--size=" + currentDownLoaded);
            // 关闭各种流操作
            currentPart.close();
            in.close();
            connection.disconnect();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            MoreThreadUtils.readestTask--;
            if (MoreThreadUtils.readestTask == 0) {// 当前下载的线程数量为0,下载完成
                Log.d(TAG, "doHttpTask: 下载完成");

//                installApk(context,targetFilePathAndName);
            }
        }
    }

   
 

    interface IUpdateProgress {
        void update(int progress);
    }
}

发布了118 篇原创文章 · 获赞 16 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/chentaishan/article/details/104941142